feat: add notification to contact response

This commit is contained in:
2025-12-21 00:10:35 -03:00
parent e1b1f6d743
commit 3b5205e124
4 changed files with 115 additions and 10 deletions

View File

@@ -0,0 +1,53 @@
import { TestBed } from '@angular/core/testing';
import { ResponseStateNotification } from './ResponseStateNotificatio';
import { Notifier } from './notifier';
import { catchError, EMPTY, of, throwError } from 'rxjs';
import { Notification } from '../models/Notification';
describe('ResponseStateNotificatio', () => {
let service: ResponseStateNotification;
let notifier: jasmine.SpyObj<Notifier>;
let NOTIFICATION_MOCK: Notification;
beforeEach(() => {
notifier = jasmine.createSpyObj(Notifier.name, ['notify']);
NOTIFICATION_MOCK = new Notification('mock', 'success');
TestBed.configureTestingModule({
providers: [{ provide: Notifier, useValue: notifier }],
});
service = TestBed.inject(ResponseStateNotification);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should notify success if observable has no errors', () => {
service
.handleResponse(
of({
data: null,
message: 'success',
success: true,
}),
NOTIFICATION_MOCK.message,
NOTIFICATION_MOCK.message
)
.subscribe();
expect(notifier.notify).toHaveBeenCalledOnceWith(NOTIFICATION_MOCK);
});
it('should notify error if observable throws error', () => {
service
.handleResponse(
throwError(() => new Error('Stream errored!')),
NOTIFICATION_MOCK.message,
NOTIFICATION_MOCK.message
)
.pipe(catchError(() => of(EMPTY)))
.subscribe();
NOTIFICATION_MOCK.type = 'error';
expect(notifier.notify).toHaveBeenCalledOnceWith(NOTIFICATION_MOCK);
});
});

View File

@@ -0,0 +1,26 @@
import { catchError, Observable, tap, throwError } from 'rxjs';
import { Response } from '../models/Response';
import { Notification } from '../models/Notification';
import { inject, Injectable } from '@angular/core';
import { Notifier } from './notifier';
@Injectable({
providedIn: 'root',
})
export class ResponseStateNotification {
private readonly notifier = inject(Notifier);
handleResponse<T>(
r: Observable<Response<T>>,
success: string,
error: string
): Observable<Response<T>> {
return r.pipe(
tap(() => this.notifier.notify(new Notification(success, 'success'))),
catchError((err) => {
this.notifier.notify(new Notification(error, 'error'));
return throwError(() => err);
})
);
}
}

View File

@@ -4,19 +4,29 @@ import { environment } from '../../environments/environment';
import { ContactDTO } from '../models/ContactDTO';
import { Response } from '../models/Response';
import { BehaviorSubject, map, switchMap, tap } from 'rxjs';
import { ResponseStateNotification } from './ResponseStateNotificatio';
import { strings } from '../strings';
@Injectable({
providedIn: 'root',
})
export class ContactService {
private readonly httpClient = inject(HttpClient);
private readonly responseStateNotification = inject(ResponseStateNotification);
private readonly contacts = new BehaviorSubject<ContactDTO[]>([]);
readonly contacts$ = this.contacts.asObservable();
delete(id: number) {
return this.httpClient
.delete<Response<string>>(`${environment.apiUrl}/contacts/${id}`)
.pipe(switchMap(() => this.getAll()));
return this.httpClient.delete<Response<string>>(`${environment.apiUrl}/contacts/${id}`).pipe(
(r) =>
this.responseStateNotification.handleResponse(
r,
strings.deleteContactSuccessNotification,
strings.errorNotification
),
switchMap(() => this.getAll())
);
}
findById(id: string) {
@@ -33,15 +43,27 @@ export class ContactService {
}
save(contact: ContactDTO) {
return this.httpClient
.post<Response<ContactDTO[] | null>>(environment.apiUrl + '/contacts', contact)
.pipe(switchMap(() => this.getAll()));
return this.httpClient.post<any>(environment.apiUrl + '/contacts', contact).pipe(
(r) =>
this.responseStateNotification.handleResponse(
r,
strings.createContactSuccessNotification,
strings.errorNotification
),
switchMap(() => this.getAll())
);
}
update(contact: ContactDTO) {
return this.httpClient.put<Response<ContactDTO[] | null>>(
`${environment.apiUrl}/contacts/${contact.id}`,
contact
);
return this.httpClient
.put<Response<ContactDTO[] | null>>(`${environment.apiUrl}/contacts/${contact.id}`, contact)
.pipe((r) =>
this.responseStateNotification.handleResponse(
r,
strings.editContactSuccessNotification,
strings.errorNotification
)
);
}
}

View File

@@ -9,11 +9,15 @@ export const strings = Object.freeze({
contactsCompany: "Contact's company",
contactsPhone: "Contact's phone",
contactList: 'contact list',
createContactSuccessNotification: 'Contact created successfully',
deleteContactSuccessNotification: 'Contact deleted successfully',
editContact: 'edit contact',
editContactSuccessNotification: 'Contact edited successfully',
editTheContact: 'edit the contact',
errorMessageMaxLength: (maxLen: number) => `Must be ${maxLen} characters or fewer.`,
errorMessagePhonePattern: `Valid format: + (optional) plus 12 to 15 digits`,
errorMessageRequired: 'This field is required.',
errorNotification: 'Oops, there was an error',
name: 'name',
phone: 'phone',
save: 'save',