diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-list-resource.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-list-resource.service.ts index 1378b74475c88940614acdf93cd68218ee9927ea..ab781c8a7689b110648eda5cccda1e6cee46a801 100644 --- a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-list-resource.service.ts +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-list-resource.service.ts @@ -1,5 +1,10 @@ import { ConfigurationLinkRel, ConfigurationResource, ConfigurationService } from '@admin-client/configuration-shared'; -import { ListResourceServiceConfig, ResourceListService, ResourceRepository } from '@alfa-client/tech-shared'; +import { + ApiListResourceService, + ListResourceServiceConfig, + ResourceListService, + ResourceRepository, +} from '@alfa-client/tech-shared'; import { AggregationMappingListLinkRel } from './aggregation-mapping.linkrel'; import { AggregationMappingListResource, AggregationMappingResource } from './aggregation-mapping.model'; @@ -13,14 +18,14 @@ export function createAggregationMappingListResourceService( repository: ResourceRepository, configurationService: ConfigurationService, ) { - return new ResourceListService(buildConfig(configurationService), repository); + return new ApiListResourceService(buildConfig(configurationService), repository); } function buildConfig(configurationService: ConfigurationService): ListResourceServiceConfig<ConfigurationResource> { return { baseResource: configurationService.get(), - listLinkRel: ConfigurationLinkRel.AGGREGATION_MAPPINGS, listResourceListLinkRel: AggregationMappingListLinkRel.LIST, - createLinkRel: AggregationMappingListLinkRel.SELF, + getLinkRel: ConfigurationLinkRel.AGGREGATION_MAPPINGS, + create: { linkRel: AggregationMappingListLinkRel.SELF }, }; } diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.spec.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.spec.ts index d9131bbba356d5498790a473a7429705a8f3b92e..5730eb0567bceaef68a753e8d4f7f7e08aad7332 100644 --- a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.spec.ts +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.spec.ts @@ -60,7 +60,7 @@ describe('AggregationMappingResourceService', () => { useFromMock(navigationService), ); - expect(config.resource).toBeObservable(singleColdCompleted(staticResource)); + expect(config.baseResource).toBeObservable(singleColdCompleted(staticResource)); }); }); diff --git a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts index 0bfa0689a8c24df11542605a2f33dfbe4a4401eb..8ac05193e094e6cd2caad8e54998a1a24216152d 100644 --- a/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts +++ b/alfa-client/libs/admin/reporting-shared/src/lib/aggregation-mapping-resource.service.ts @@ -8,6 +8,7 @@ import { ResourceRepository, ResourceServiceConfig, StateResource, + StateService, } from '@alfa-client/tech-shared'; import { UrlSegment } from '@angular/router'; import { iif, map, Observable, of, switchMap } from 'rxjs'; @@ -22,9 +23,14 @@ export class AggregationMappingResourceService extends ApiResourceService< export function createAggregationMappingResourceService( repository: ResourceRepository, + stateService: StateService, navigationService: NavigationService, ): AggregationMappingResourceService { - return new AggregationMappingResourceService(_buildResourceServiceConfig(repository, navigationService), repository); + return new AggregationMappingResourceService( + _buildResourceServiceConfig(repository, navigationService), + stateService, + repository, + ); } export function _buildResourceServiceConfig( @@ -32,7 +38,7 @@ export function _buildResourceServiceConfig( navigationService: NavigationService, ): ResourceServiceConfig<AggregationMappingResource> { return { - resource: self._getResourceByNavigationRoute(repository, navigationService), + baseResource: self._getResourceByNavigationRoute(repository, navigationService), getLinkRel: AggregationMappingLinkRel.SELF, edit: { linkRel: AggregationMappingLinkRel.SELF }, delete: { linkRel: AggregationMappingLinkRel.SELF }, diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.spec.ts index 87451e3301ee1530a3e6a5ecda5dac37d179d979..4b619c70e46c8e995c0787faa4157aaa6bb321c6 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.spec.ts @@ -1,13 +1,23 @@ +import { ProblemDetail } from '@alfa-client/tech-shared'; import { mock, Mock, useFromMock } from '@alfa-client/test-utils'; import { Resource } from '@ngxp/rest'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { cold } from 'jest-marbles'; +import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; import { DummyLinkRel, DummyListLinkRel } from '../../../test/dummy'; -import { multipleCold, singleCold } from '../../../test/marbles'; +import { createProblemDetail } from '../../../test/error'; +import { multipleCold, singleCold, singleColdCompleted } from '../../../test/marbles'; import { createDummyListResource, createDummyResource } from '../../../test/resource'; import { ApiListResourceService } from './api-list-resource.service'; import { CreateResourceData, LinkRelationName, ListItemResource, ListResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; -import { createEmptyStateResource, createStateResource, ListResource, StateResource } from './resource.util'; +import { + createEmptyStateResource, + createErrorStateResource, + createLoadingStateResource, + createStateResource, + ListResource, + StateResource, +} from './resource.util'; describe('ListResourceService', () => { let service: ApiListResourceService<Resource, ListResource, ListItemResource>; @@ -24,6 +34,7 @@ describe('ListResourceService', () => { const baseResourceSubj: BehaviorSubject<StateResource<Resource>> = new BehaviorSubject<StateResource<Resource>>( baseStateResource, ); + beforeEach(() => { config = { baseResource: baseResourceSubj, @@ -81,5 +92,39 @@ describe('ListResourceService', () => { multipleCold(createEmptyStateResource(true), createStateResource(returnResource)), ); }); + + it('should handle error', () => { + const error: ProblemDetail = createProblemDetail(); + service._handleError = jest.fn(); + resourceRepository.createResource.mockReturnValue(throwError(() => error)); + + service.create(toCreate).subscribe(); + + expect(service._handleError).toHaveBeenCalledWith(error); + }); + + it('should emit on error', () => { + const error: ProblemDetail = createProblemDetail(); + service._handleError = jest.fn().mockReturnValue(of(createErrorStateResource(error))); + resourceRepository.createResource.mockReturnValue(cold('-#', null, error)); + + expect(service.create(toCreate)).toBeObservable( + cold('a(b|)', { a: createLoadingStateResource(), b: createErrorStateResource(error) }), + ); + }); + }); + + describe('handle error', () => { + it('should return error state resource on unprocessable entity', () => { + const error: ProblemDetail = createProblemDetail(); + + expect(service._handleError(error)).toBeObservable(singleColdCompleted(createErrorStateResource(error))); + }); + + it('should throw error', () => { + const error: ProblemDetail = { ...createProblemDetail(), status: 500 }; + + expect(service._handleError(error)).toBeObservable(cold('#', null, error)); + }); }); }); diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.ts index 03d1d46175587de5bc958432a570c4e6d32c3081..1bedc05a6f4f0e0cc6f20fde5b932eb2e821f6e8 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/api-list-resource.service.ts @@ -1,9 +1,17 @@ import { Resource } from '@ngxp/rest'; -import { first, map, Observable, startWith, switchMap } from 'rxjs'; +import { catchError, first, map, Observable, of, startWith, switchMap, throwError } from 'rxjs'; +import { isUnprocessableEntity } from '../http.util'; +import { ProblemDetail } from '../tech.model'; import { ResourceListService } from './list-resource.service'; import { CreateResourceData, ListItemResource, ListResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; -import { createEmptyStateResource, createStateResource, ListResource, StateResource } from './resource.util'; +import { + createEmptyStateResource, + createErrorStateResource, + createStateResource, + ListResource, + StateResource, +} from './resource.util'; export class ApiListResourceService< B extends Resource, @@ -25,10 +33,18 @@ export class ApiListResourceService< this.repository.createResource(this.buildCreateResourceData(toCreate, this.config.create.linkRel, listResource.resource)), ), map((listItemResource: I) => createStateResource(listItemResource)), + catchError((error: ProblemDetail) => this._handleError(error)), startWith(createEmptyStateResource<I>(true)), ); } + _handleError(error: ProblemDetail): Observable<StateResource<I>> { + if (isUnprocessableEntity(error.status)) { + return of(createErrorStateResource(error)); + } + return throwError(() => error); + } + private verifyBeforeCreation(): void { this.verifyValidListResource(); this.throwErrorOn(!this.isCreateLinkPresent(), 'No creation link exists.');