diff --git a/alfa-client/libs/admin-settings/src/lib/admin-settings.module.ts b/alfa-client/libs/admin-settings/src/lib/admin-settings.module.ts index 5a004145d5a096188ccf9c4b0db97a6345f090da..4e807a8a58633b55907f22310ec0be38f9bb3da0 100644 --- a/alfa-client/libs/admin-settings/src/lib/admin-settings.module.ts +++ b/alfa-client/libs/admin-settings/src/lib/admin-settings.module.ts @@ -14,12 +14,14 @@ import { } from './admin-settings-resource.service'; import { SettingsService } from './admin-settings.service'; import { - CONFIGURATION_FEATURE_KEY, - configurationReducers, ConfigurationResourceService, createConfigurationResourceService, } from './configuration/configuration-resource.service'; import { ConfigurationService } from './configuration/configuration.service'; +import { + CONFIGURATION_FEATURE_KEY, + configurationReducers, +} from './configuration/configuration.state'; import { NavigationComponent } from './navigation/navigation.component'; import { OrganisationseinheitContainerComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-container.component'; import { OrganisationseinheitFormComponent } from './organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component'; @@ -30,11 +32,10 @@ import { PostfachFormComponent } from './postfach/postfach-container/postfach-fo import { PostfachNavigationItemComponent } from './postfach/postfach-navigation-item/postfach-navigation-item.component'; import { createPostfachResourceService, - POSTFACH_FEATURE_KEY, - postfachReducers, PostfachResourceService, } from './postfach/postfach-resource.service'; import { PostfachService } from './postfach/postfach.service'; +import { POSTFACH_FEATURE_KEY, postfachReducers } from './postfach/postfach.state'; import { MoreItemButtonComponent } from './shared/more-menu/more-item-button/more-item-button.component'; import { MoreMenuComponent } from './shared/more-menu/more-menu.component'; import { NavigationItemComponent } from './shared/navigation-item/navigation-item.component'; diff --git a/alfa-client/libs/admin-settings/src/lib/configuration/configuration-resource.service.ts b/alfa-client/libs/admin-settings/src/lib/configuration/configuration-resource.service.ts index 6a90a9b2cf2a3341066fcf0de75fa92eba3f3190..2c48b189fde24bbec25fff8b832022f4c27bc5e4 100644 --- a/alfa-client/libs/admin-settings/src/lib/configuration/configuration-resource.service.ts +++ b/alfa-client/libs/admin-settings/src/lib/configuration/configuration-resource.service.ts @@ -1,19 +1,12 @@ import { ApiRootLinkRel, ApiRootResource, ApiRootService } from '@alfa-client/api-root-shared'; import { ApiResourceService, - ResourceReducer, ResourceRepository, ResourceServiceConfig, - ResourceState, - createResourceAction, + StateService, } from '@alfa-client/tech-shared'; -import { Action, ActionReducerMap } from '@ngrx/store'; -import { StateService } from 'libs/tech-shared/src/lib/ngrx/resource.state.service'; import { ConfigurationResource } from './configuration.model'; - -export const CONFIGURATION_FEATURE_KEY = 'ConfigurationState'; - -export const CONFIGURATION_PATH = 'configuration'; +import { CONFIGURATION_FEATURE_KEY, CONFIGURATION_PATH } from './configuration.state'; export class ConfigurationResourceService extends ApiResourceService< ApiRootResource, @@ -38,13 +31,3 @@ function buildConfig(apiRootService: ApiRootService): ResourceServiceConfig<ApiR getLinkRel: ApiRootLinkRel.CONFIGURATION, }; } - -export function configurationResourceReducer(state: ResourceState, action: Action) { - const resourceReducer: ResourceReducer = new ResourceReducer( - createResourceAction(CONFIGURATION_FEATURE_KEY), - ); - return resourceReducer.reducer(state, action); -} -export const configurationReducers: ActionReducerMap<any> = { - [CONFIGURATION_PATH]: configurationResourceReducer, -}; diff --git a/alfa-client/libs/admin-settings/src/lib/configuration/configuration.state.ts b/alfa-client/libs/admin-settings/src/lib/configuration/configuration.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa9738fba4e1f93c66e858c0d045dedc2f25430b --- /dev/null +++ b/alfa-client/libs/admin-settings/src/lib/configuration/configuration.state.ts @@ -0,0 +1,16 @@ +import { ResourceReducer, ResourceState, createResourceActions } from '@alfa-client/tech-shared'; +import { Action, ActionReducerMap } from '@ngrx/store'; + +export const CONFIGURATION_FEATURE_KEY = 'ConfigurationState'; + +export const CONFIGURATION_PATH = 'configuration'; + +export function configurationResourceReducer(state: ResourceState, action: Action) { + const resourceReducer: ResourceReducer = new ResourceReducer( + createResourceActions(CONFIGURATION_FEATURE_KEY), + ); + return resourceReducer.reducer(state, action); +} +export const configurationReducers: ActionReducerMap<any> = { + [CONFIGURATION_PATH]: configurationResourceReducer, +}; diff --git a/alfa-client/libs/admin-settings/src/lib/postfach/postfach-resource.service.ts b/alfa-client/libs/admin-settings/src/lib/postfach/postfach-resource.service.ts index c7e1a5203f7f5d2b76c2c3783c8793972a8dd460..ef203bc239deca1b8e4d90be475f28238efbaef5 100644 --- a/alfa-client/libs/admin-settings/src/lib/postfach/postfach-resource.service.ts +++ b/alfa-client/libs/admin-settings/src/lib/postfach/postfach-resource.service.ts @@ -1,20 +1,13 @@ import { ApiResourceService, - ResourceReducer, ResourceRepository, ResourceServiceConfig, - ResourceState, - createResourceAction, } from '@alfa-client/tech-shared'; -import { Action, ActionReducerMap } from '@ngrx/store'; import { StateService } from 'libs/tech-shared/src/lib/ngrx/resource.state.service'; import { SettingsService } from '../admin-settings.service'; import { PostfachLinkRel } from './postfach.linkrel'; import { PostfachResource } from './postfach.model'; - -export const POSTFACH_FEATURE_KEY = 'PostfachState'; - -export const POSTFACH_PATH = 'postfach'; +import { POSTFACH_FEATURE_KEY, POSTFACH_PATH } from './postfach.state'; export class PostfachResourceService extends ApiResourceService< PostfachResource, @@ -37,13 +30,3 @@ function buildConfig(settingService: SettingsService): ResourceServiceConfig<Pos edit: { linkRel: PostfachLinkRel.SELF }, }; } - -export function postfachResourceReducer(state: ResourceState, action: Action) { - const resourceReducer: ResourceReducer = new ResourceReducer( - createResourceAction(POSTFACH_FEATURE_KEY), - ); - return resourceReducer.reducer(state, action); -} -export const postfachReducers: ActionReducerMap<any> = { - [POSTFACH_PATH]: postfachResourceReducer, -}; diff --git a/alfa-client/libs/admin-settings/src/lib/postfach/postfach.state.ts b/alfa-client/libs/admin-settings/src/lib/postfach/postfach.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..0084f3b240eec0afe967896f607b3778b803dcb1 --- /dev/null +++ b/alfa-client/libs/admin-settings/src/lib/postfach/postfach.state.ts @@ -0,0 +1,16 @@ +import { ResourceReducer, ResourceState, createResourceActions } from '@alfa-client/tech-shared'; +import { Action, ActionReducerMap } from '@ngrx/store'; + +export const POSTFACH_FEATURE_KEY = 'PostfachState'; + +export const POSTFACH_PATH = 'postfach'; + +export function postfachResourceReducer(state: ResourceState, action: Action) { + const resourceReducer: ResourceReducer = new ResourceReducer( + createResourceActions(POSTFACH_FEATURE_KEY), + ); + return resourceReducer.reducer(state, action); +} +export const postfachReducers: ActionReducerMap<any> = { + [POSTFACH_PATH]: postfachResourceReducer, +}; diff --git a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts index 824344e4e489e869cce0a34abb55c3607599a0ed..c944a700d3b6011a5d85b11289e11b5e9509c78c 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/+state/bescheid.reducer.ts @@ -10,7 +10,7 @@ import { StateResource, createEmptyStateResource, createErrorStateResource, - createResourceAction, + createResourceActions, createStateResource, } from '@alfa-client/tech-shared'; import { HttpErrorResponse } from '@angular/common/http'; @@ -72,7 +72,7 @@ export function reducer(state: BescheidState, action: Action): BescheidState { export function bescheidResourceReducer(state: BescheidState, action: Action) { const resourceReducer: ResourceReducer = new ResourceReducer( - createResourceAction(BESCHEID_FEATURE_KEY), + createResourceActions(BESCHEID_FEATURE_KEY), ); return resourceReducer.reducer(state, action); } diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts index 6bd7bb36ea6ecb5497a32c1e1c001be5b7ee20e2..36b2081d46f4d01b111f68dec8455173ab4c6294 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.spec.ts @@ -150,7 +150,7 @@ describe('BescheidService', () => { beforeEach(() => { facade.getBescheidCommand.mockReturnValue(of(commandStateResource)); - service.bescheidDraftService.setResourceByUri = jest.fn(); + service.bescheidDraftService.reloadByResourceUri = jest.fn(); }); it('should call facade', () => { @@ -165,7 +165,7 @@ describe('BescheidService', () => { it('should set resource by uri', () => { service.createBescheid(vorgangWithEingang).pipe(first()).subscribe(); - expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith( + expect(service.bescheidDraftService.reloadByResourceUri).toHaveBeenCalledWith( getUrl(command, CommandLinkRel.EFFECTED_RESOURCE), ); }); @@ -414,7 +414,7 @@ describe('BescheidService', () => { .fn() .mockReturnValue(of(createStateResource(bescheidResource))); commandService.createCommandByProps.mockReturnValue(of(commandStateResource)); - service.bescheidDraftService.setResourceByUri = jest.fn(); + service.bescheidDraftService.reloadByResourceUri = jest.fn(); }); it('should build update bescheid command props', () => { @@ -441,7 +441,7 @@ describe('BescheidService', () => { .updateBescheid(bescheid) .pipe(first()) .subscribe((commandStateResource: StateResource<CommandResource>) => { - expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith( + expect(service.bescheidDraftService.reloadByResourceUri).toHaveBeenCalledWith( getUrl(commandStateResource.resource, CommandLinkRel.EFFECTED_RESOURCE), ); done(); diff --git a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts index 17b847e57442bcd113a797ab356ac3f4fce9a613..813eb9eb74f9f3dc2256543f1cbd380c59129079 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -257,7 +257,7 @@ export class BescheidService { } private updateBescheidDraft(command: CommandResource): void { - this.bescheidDraftService.setResourceByUri(getEffectedResourceUrl(command)); + this.bescheidDraftService.reloadByResourceUri(getEffectedResourceUrl(command)); } public getAttachments(): Observable<BinaryFileResource[]> { diff --git a/alfa-client/libs/command-shared/src/lib/command-resource.service.ts b/alfa-client/libs/command-shared/src/lib/command-resource.service.ts index 2916c3b28a6b38c007c0c768e2ef29d0b043b7e5..3e7de70e124c5088529603df342cb83226e42ae3 100644 --- a/alfa-client/libs/command-shared/src/lib/command-resource.service.ts +++ b/alfa-client/libs/command-shared/src/lib/command-resource.service.ts @@ -26,9 +26,9 @@ export class CommandResourceService<B extends Resource, T extends Resource> exte } public delete(): Observable<StateResource<CommandResource>> { - return this.getResource().pipe( + return this.selectResource().pipe( switchMap((stateResource: StateResource<T>) => this.doDelete(stateResource.resource)), - tapOnCommandSuccessfullyDone(() => this.clearStateResource()), + tapOnCommandSuccessfullyDone(() => this.resourceStateService.clearResource()), filter(isStateResoureStable), startWith(createEmptyStateResource<CommandResource>(true)), ); diff --git a/alfa-client/libs/tech-shared/src/index.ts b/alfa-client/libs/tech-shared/src/index.ts index 2742a9d0d8892934c599355c84806d3fcfe048d6..4cea35d10a9f276badbf29c48538e1902a398959 100644 --- a/alfa-client/libs/tech-shared/src/index.ts +++ b/alfa-client/libs/tech-shared/src/index.ts @@ -33,6 +33,7 @@ export * from './lib/message-code'; export * from './lib/ngrx/actions'; export * from './lib/ngrx/del.resource.reducer'; export * from './lib/ngrx/resource.state'; +export * from './lib/ngrx/resource.state.service'; export * from './lib/pipe/convert-for-data-test.pipe'; export * from './lib/pipe/convert-to-boolean.pipe'; export * from './lib/pipe/enum-to-label.pipe'; diff --git a/alfa-client/libs/tech-shared/src/lib/ngrx/actions.ts b/alfa-client/libs/tech-shared/src/lib/ngrx/actions.ts index b72e3d41ece3c13fd66dff1977c8755ab545d065..88af729d21fdee66f88b0c716225aadb4bcc4bdf 100644 --- a/alfa-client/libs/tech-shared/src/lib/ngrx/actions.ts +++ b/alfa-client/libs/tech-shared/src/lib/ngrx/actions.ts @@ -51,16 +51,18 @@ export interface LoadResourceFailureProps { export interface LoadResourceSuccessProps {} -export interface ResourceActions { +export interface ResourceLoadActions { loadAction: TypedActionCreatorWithProps<ResourceUriProps>; loadSuccessAction: TypedActionCreatorWithProps<LoadResourceSuccessProps>; loadFailureAction: TypedActionCreatorWithProps<LoadResourceFailureProps>; +} +export interface ResourceActions extends ResourceLoadActions { clearAction: TypedActionCreator; reloadAction: TypedActionCreator; } -export function createResourceAction(featureName: string): ResourceActions { +export function createResourceActions(featureName: string): ResourceActions { const loadAction: TypedActionCreatorWithProps<ResourceUriProps> = createAction( createActionType(featureName, 'Load Resource'), props<ResourceUriProps>(), diff --git a/alfa-client/libs/tech-shared/src/lib/ngrx/resource.state.service.ts b/alfa-client/libs/tech-shared/src/lib/ngrx/resource.state.service.ts index 1c03f24ccc953ba31984d55f91a96000d257db53..6260501038632981305670f82428daa8d20412e0 100644 --- a/alfa-client/libs/tech-shared/src/lib/ngrx/resource.state.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/ngrx/resource.state.service.ts @@ -1,10 +1,19 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { ResourceUri } from '@ngxp/rest'; -import { Observable } from 'rxjs'; -import { StateInfo } from '../resource/resource.model'; -import { StateResource } from '../resource/resource.util'; -import { ResourceActions, createResourceAction } from './actions'; +import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; +import { isEqual, isNull } from 'lodash-es'; +import { Observable, combineLatest, filter, startWith, tap } from 'rxjs'; +import { ResourceServiceConfig, StateInfo } from '../resource/resource.model'; +import { mapToFirst, mapToResource } from '../resource/resource.rxjs.operator'; +import { + StateResource, + createEmptyStateResource, + isInvalidResourceCombination, + isLoadingRequired, + isStateResoureStable, +} from '../resource/resource.util'; +import { isNotNull } from '../tech.util'; +import { ResourceActions, createResourceActions } from './actions'; import { EffectService } from './effects.service'; import * as ResourceSelectors from '../ngrx/resource.selector'; @@ -28,43 +37,123 @@ export class ResourceStateService { private stateInfo: StateInfo, private store: Store, private effectService: EffectService, - ) {} + ) { + this.init(); + } public init() { - this.createActions(); + this.initActions(); this.initEffects(); + return this; } - private createActions(): void { - this.actions = createResourceAction(this.stateInfo.name); + private initActions(): void { + this.actions = createResourceActions(this.stateInfo.name); } private initEffects(): void { this.effectService.addEffects(this.actions); } - getActions(): ResourceActions { - return this.actions; - } - - clear(): void { + public clearResource(): void { this.store.dispatch(this.actions.clearAction()); } - load(resourceUri: ResourceUri): void { + public loadResource(resourceUri: ResourceUri): void { this.store.dispatch(this.actions.loadAction({ resourceUri })); } - reload(): void { + public reloadResource(): void { this.store.dispatch(this.actions.reloadAction()); } - getResource<T>(): Observable<StateResource<T>> { + public selectResource<T>(): Observable<StateResource<T>> { return this.store.select(ResourceSelectors.selectResource(this.stateInfo)); } - existsResource(): Observable<boolean> { + public existsResource(): Observable<boolean> { return this.store.select(ResourceSelectors.existResource(this.stateInfo)); } } + +export class ResourceLoader<B extends Resource, T extends Resource> { + configResource: B = null; + + constructor( + private config: ResourceServiceConfig<B>, + private resourceStateService: ResourceStateService, + ) {} + + public load() { + return combineLatest([ + this.resourceStateService.selectResource(), + this.getConfigResource(), + ]).pipe( + tap(([stateResource, configResource]) => + this.handleConfigResourceChanged(<StateResource<T>>stateResource, configResource), + ), + filter( + ([stateResource, configResource]) => + !isInvalidResourceCombination(<StateResource<T>>stateResource, configResource), + ), + mapToFirst<T, B>(), + startWith(createEmptyStateResource<T>(true)), + ); + } + + private getConfigResource(): Observable<B> { + return this.config.resource.pipe(filter(isStateResoureStable), mapToResource<B>()); + } + + handleConfigResourceChanged(stateResource: StateResource<T>, configResource: B): void { + if (!isEqual(this.configResource, configResource)) { + this.configResource = configResource; + this.updateStateResourceByConfigResource(stateResource, configResource); + } else if (this.shouldLoadResource(stateResource, configResource)) { + this.loadResource(configResource); + } + } + + updateStateResourceByConfigResource(stateResource: StateResource<T>, configResource: B): void { + if (!isStateResoureStable(stateResource)) { + return; + } + if (this.shouldClearStateResource(stateResource, configResource)) { + this.resourceStateService.clearResource(); + } else if (isNotNull(configResource) && hasLink(configResource, this.config.getLinkRel)) { + this.loadResource(configResource); + } + } + + shouldClearStateResource(stateResource: StateResource<T>, configResource: B): boolean { + return ( + (isNull(configResource) || this.hasNotGetLink(configResource)) && + !this.isStateResourceEmpty(stateResource) + ); + } + + private hasNotGetLink(configResource: B) { + return isNotNull(configResource) && !hasLink(configResource, this.config.getLinkRel); + } + + private isStateResourceEmpty(stateResource: StateResource<T>): boolean { + return isEqual(stateResource, createEmptyStateResource()); + } + + shouldLoadResource(stateResource: StateResource<T>, configResource: B): boolean { + return ( + isNotNull(configResource) && + hasLink(configResource, this.config.getLinkRel) && + isLoadingRequired(stateResource) + ); + } + + loadResource(configResource: B): void { + this.resourceStateService.loadResource(this.getGetUrl(configResource)); + } + + private getGetUrl(configResource: B): string { + return getUrl(configResource, this.config.getLinkRel); + } +} diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts index 5fc2b11bc89e1479144998213b83128f6196963e..19ec27f814d90f920c3c7ba4fc0d63440fcce091 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.ts @@ -1,12 +1,18 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Resource } from '@ngxp/rest'; -import { Observable, catchError, map, startWith, switchMap } from 'rxjs'; +import { Observable, catchError, map, of, startWith, switchMap, throwError } from 'rxjs'; +import { isUnprocessableEntity } from '../http.util'; import { StateService } from '../ngrx/resource.state.service'; import { HttpError } from '../tech.model'; import { ResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; import { ResourceService } from './resource.service'; -import { StateResource, createEmptyStateResource, createStateResource } from './resource.util'; +import { + StateResource, + createEmptyStateResource, + createErrorStateResource, + createStateResource, +} from './resource.util'; export class ApiResourceService<B extends Resource, T extends Resource> extends ResourceService< B, @@ -21,7 +27,7 @@ export class ApiResourceService<B extends Resource, T extends Resource> extends } public save(toSave: unknown): Observable<StateResource<T | HttpError>> { - return this.getResource().pipe( + return this.selectResource().pipe( switchMap((stateResource: StateResource<T>) => this.doSave(stateResource.resource, toSave).pipe( catchError((errorResponse: HttpErrorResponse) => this.handleError(errorResponse)), @@ -40,4 +46,11 @@ export class ApiResourceService<B extends Resource, T extends Resource> extends }) .pipe(map((value: T) => createStateResource<T>(value))); } + + handleError(errorResponse: HttpErrorResponse): Observable<StateResource<HttpError>> { + if (isUnprocessableEntity(errorResponse.status)) { + return of(createErrorStateResource((<any>errorResponse) as HttpError)); + } + return throwError(() => errorResponse); + } } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts index a0d8b5fec5007d6405999dc1d3bf865511277589..0a4dfa4f9050e29f527d0143c188116ab3a99fcb 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/list-resource.service.ts @@ -1,21 +1,15 @@ -import { HttpErrorResponse } from '@angular/common/http'; import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; import { isEqual, isNull } from 'lodash-es'; import { BehaviorSubject, Observable, - catchError, combineLatest, filter, first, map, - of, startWith, tap, - throwError, } from 'rxjs'; -import { isUnprocessableEntity } from '../http.util'; -import { HttpError } from '../tech.model'; import { isNotNull, isNotUndefined } from '../tech.util'; import { CreateResourceData, ListItemResource, ListResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; @@ -24,7 +18,6 @@ import { ListResource, StateResource, createEmptyStateResource, - createErrorStateResource, createStateResource, getEmbeddedResources, isEmptyStateResource, @@ -250,39 +243,4 @@ export class ResourceListService< ), ); } - - public saveItem( - itemResource: ListItemResource, - toSave: unknown, - ): Observable<StateResource<any | HttpError>> { - return this.doSave(itemResource, toSave).pipe( - // filter(isLoaded), - tap((response: StateResource<T>) => { - // if (isLoaded(response)) { - console.info('Saved Response: ', response); - // this.loadListResource(this.getListResource(), linkRel); - // this.listResource.next({ ...this.listResource.value, reload: true }); - // } - }), - catchError((errorResponse: HttpErrorResponse) => this.handleError(errorResponse)), - startWith(createEmptyStateResource<T | HttpError>(true)), - ); - } - - handleError(errorResponse: HttpErrorResponse): Observable<StateResource<HttpError>> { - if (isUnprocessableEntity(errorResponse.status)) { - return of(createErrorStateResource((<any>errorResponse) as HttpError)); - } - return throwError(() => errorResponse); - } - - doSave(resource: ListItemResource, toSave: unknown): Observable<StateResource<T>> { - return this.repository - .save({ - resource, - linkRel: 'self', - toSave, - }) - .pipe(map((value: T) => createStateResource<T>(value))); - } } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.model.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.model.ts index 79afc9e0b94cbe326b4b40c355ac50e2f3d2d084..f4e537398c9ae4cec27673b53417b50657c9f1c4 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.model.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.model.ts @@ -31,7 +31,7 @@ export interface StateInfo { } export interface ResourceServiceConfig<B> { - stateInfo?: StateInfo; + stateInfo: StateInfo; resource: Observable<StateResource<B>>; getLinkRel: LinkRelationName; delete?: { linkRel: LinkRelationName; order?: string }; diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts index bd723a6f1a9d1cbd76581273b76e3a36f2e09122..3dbd32e44c7c8c6803abdff1c1ee4a6232882314 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.spec.ts @@ -320,12 +320,12 @@ describe('ResourceService', () => { }); }); - describe('setResourceByUri', () => { + describe('reloadByResourceUri', () => { it('should do load resource', () => { service.doLoadResource = jest.fn(); const resourceUri: ResourceUri = faker.internet.url(); - service.setResourceByUri(resourceUri); + service.reloadByResourceUri(resourceUri); expect(service.doLoadResource).toHaveBeenCalledWith(resourceUri); }); diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts index 6ca741504cbfce44d903f5645a18c5e3ca1b6d56..254db478b683054a314933c42c52649d7a0865e9 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.ts @@ -1,139 +1,47 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Resource, ResourceUri, getUrl, hasLink } from '@ngxp/rest'; -import { isEqual, isNull } from 'lodash-es'; -import { Observable, combineLatest, filter, of, startWith, tap, throwError } from 'rxjs'; -import { isUnprocessableEntity } from '../http.util'; -import { ResourceStateService, StateService } from '../ngrx/resource.state.service'; -import { HttpError } from '../tech.model'; -import { isNotNull } from '../tech.util'; +import { Resource, ResourceUri } from '@ngxp/rest'; +import { Observable } from 'rxjs'; +import { ResourceLoader, ResourceStateService, StateService } from '../ngrx/resource.state.service'; import { ResourceServiceConfig } from './resource.model'; -import { mapToFirst, mapToResource } from './resource.rxjs.operator'; -import { - StateResource, - createEmptyStateResource, - createErrorStateResource, - isInvalidResourceCombination, - isLoadingRequired, - isStateResoureStable, -} from './resource.util'; +import { StateResource } from './resource.util'; /** * B = Type of baseresource * T = Type of the resource which is working on */ export abstract class ResourceService<B extends Resource, T extends Resource> { - configResource: B = null; - resourceStateService: ResourceStateService; + resourceLoader: ResourceLoader<B, T>; constructor( protected config: ResourceServiceConfig<B>, protected stateService: StateService, ) { this.initStateService(); + this.initResourceLoader(); } - initStateService(): void { + private initStateService(): void { this.resourceStateService = this.stateService.createInstance(this.config.stateInfo); } - public get(): Observable<StateResource<T>> { - return combineLatest([this.getResource(), this.getConfigResource()]).pipe( - tap(([stateResource, configResource]) => - this.handleConfigResourceChanged(<StateResource<T>>stateResource, configResource), - ), - filter( - ([stateResource, configResource]) => - !isInvalidResourceCombination(<StateResource<T>>stateResource, configResource), - ), - mapToFirst<T, B>(), - startWith(createEmptyStateResource<T>(true)), - ); - } - - private getConfigResource(): Observable<B> { - return this.config.resource.pipe(filter(isStateResoureStable), mapToResource<B>()); + private initResourceLoader(): void { + this.resourceLoader = new ResourceLoader<B, T>(this.config, this.resourceStateService); } - handleConfigResourceChanged(stateResource: StateResource<T>, configResource: B): void { - if (!isEqual(this.configResource, configResource)) { - this.configResource = configResource; - this.updateStateResourceByConfigResource(stateResource, configResource); - } else if (this.shouldLoadResource(stateResource, configResource)) { - this.loadResource(configResource); - } - } - - //TDD - updateStateResourceByConfigResource(stateResource: StateResource<T>, configResource: B): void { - if (!isStateResoureStable(stateResource)) { - return; - } - if (this.shouldClearStateResource(stateResource, configResource)) { - this.clearStateResource(); - } else if (isNotNull(configResource) && hasLink(configResource, this.config.getLinkRel)) { - this.loadResource(configResource); - } - } - - clearStateResource(): void { - this.resourceStateService.clear(); - } - // - - shouldClearStateResource(stateResource: StateResource<T>, configResource: B): boolean { - return ( - (isNull(configResource) || this.hasNotGetLink(configResource)) && - !this.isStateResourceEmpty(stateResource) - ); - } - - private hasNotGetLink(configResource: B) { - return isNotNull(configResource) && !hasLink(configResource, this.config.getLinkRel); - } - - private isStateResourceEmpty(stateResource: StateResource<T>): boolean { - return isEqual(stateResource, createEmptyStateResource()); - } - - shouldLoadResource(stateResource: StateResource<T>, configResource: B): boolean { - return ( - isNotNull(configResource) && - hasLink(configResource, this.config.getLinkRel) && - isLoadingRequired(stateResource) - ); - } - - loadResource(configResource: B): void { - this.doLoadResource(this.getGetUrl(configResource)); - } - - private getGetUrl(configResource: B): string { - return getUrl(configResource, this.config.getLinkRel); - } - - //TODO rename to reloadByResourceUri - public setResourceByUri(resourceUri: ResourceUri): void { - this.doLoadResource(resourceUri); - } - - doLoadResource(resourceUri: ResourceUri): void { - this.resourceStateService.load(resourceUri); + public get(): Observable<StateResource<T>> { + return this.resourceLoader.load(); } - getResource(): Observable<StateResource<T>> { - return this.resourceStateService.getResource(); + public reloadByResourceUri(resourceUri: ResourceUri): void { + this.resourceStateService.loadResource(resourceUri); } - handleError(errorResponse: HttpErrorResponse): Observable<StateResource<HttpError>> { - if (isUnprocessableEntity(errorResponse.status)) { - return of(createErrorStateResource((<any>errorResponse) as HttpError)); - } - return throwError(() => errorResponse); + public selectResource(): Observable<StateResource<T>> { + return this.resourceStateService.selectResource(); } public refresh(): void { - this.resourceStateService.reload(); + this.resourceStateService.reloadResource(); } public exists(): Observable<boolean> {