From 56b07ad986f5b96cfdf5638bd172fa7f5908a05d Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Wed, 29 May 2024 19:43:41 +0200 Subject: [PATCH] OZG-5012 remove or convert "get native data" functions of resource service; adjust bescheid service --- .../src/lib/bescheid.service.spec.ts | 56 ++-- .../src/lib/bescheid.service.ts | 45 ++- .../src/lib/command-resource.service.ts | 1 - .../lib/resource/api-resource.service.spec.ts | 41 --- .../src/lib/resource/api-resource.service.ts | 5 - .../src/lib/resource/resource.service.spec.ts | 261 +++++++++--------- .../src/lib/resource/resource.service.ts | 113 +++----- 7 files changed, 243 insertions(+), 279 deletions(-) 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 e60c95076e..844c5769fd 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 @@ -11,12 +11,12 @@ import { } from '@alfa-client/command-shared'; import { ApiError, - createEmptyStateResource, - createErrorStateResource, - createStateResource, EMPTY_STRING, HttpError, StateResource, + createEmptyStateResource, + createErrorStateResource, + createStateResource, } from '@alfa-client/tech-shared'; import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { @@ -27,12 +27,12 @@ import { } from '@alfa-client/vorgang-shared'; import { fakeAsync, tick } from '@angular/core/testing'; import faker from '@faker-js/faker'; -import { getUrl, ResourceUri } from '@ngxp/rest'; +import { ResourceUri, getUrl } from '@ngxp/rest'; import { cold } from 'jest-marbles'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; import { createApiError } from 'libs/tech-shared/test/error'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; -import { first, Observable, of } from 'rxjs'; +import { Observable, first, of } from 'rxjs'; import { createBinaryFileListResource, createBinaryFileResource, @@ -148,7 +148,7 @@ describe('BescheidService', () => { beforeEach(() => { facade.getBescheidCommand.mockReturnValue(of(commandStateResource)); - service.bescheidDraftService.setResourceByUri = jest.fn(); + service.bescheidDraftService.loadByResourceUri = jest.fn(); }); it('should call facade', () => { @@ -163,7 +163,7 @@ describe('BescheidService', () => { it('should set resource by uri', () => { service.createBescheid(vorgangWithEingang).pipe(first()).subscribe(); - expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith( + expect(service.bescheidDraftService.loadByResourceUri).toHaveBeenCalledWith( getUrl(command, CommandLinkRel.EFFECTED_RESOURCE), ); }); @@ -368,8 +368,9 @@ describe('BescheidService', () => { }); it('should return command', () => { - const command$: Observable<StateResource<CommandResource>> = - service.bescheidErstellungUeberspringen(vorgangWithEingangResource); + const command$: Observable<StateResource<CommandResource>> = service.vorgangAbschliesen( + vorgangWithEingangResource, + ); expect(command$).toBeObservable(cold('(a|)', { a: commandStateResource })); }); @@ -421,14 +422,15 @@ describe('BescheidService', () => { .mockReturnValue(createCommandProps); service.bescheidDraftService.stateResource.next(createStateResource(bescheidResource)); commandService.createCommandByProps.mockReturnValue(of(commandStateResource)); - service.bescheidDraftService.setResourceByUri = jest.fn(); + service.bescheidDraftService.loadByResourceUri = jest.fn(); + service.getResource = jest.fn().mockReturnValue(createBescheidResource()); }); it('should build update bescheid command props', () => { service.updateBescheid(bescheid); expect(buildUpdateBescheidCommandPropsSpy).toHaveBeenCalledWith( - service.bescheidDraftService.getResource(), + service.getResource(), bescheid, ); }); @@ -451,7 +453,7 @@ describe('BescheidService', () => { .updateBescheid(bescheid) .pipe(first()) .subscribe((commandStateResource: StateResource<CommandResource>) => { - expect(service.bescheidDraftService.setResourceByUri).toHaveBeenCalledWith( + expect(service.bescheidDraftService.loadByResourceUri).toHaveBeenCalledWith( getUrl(commandStateResource.resource, CommandLinkRel.EFFECTED_RESOURCE), ); done(); @@ -956,7 +958,7 @@ describe('BescheidService', () => { beforeEach(() => { commandService.createCommandByProps.mockReturnValue(of(commandStateResource)); - service.bescheidDraftService.getResource = jest.fn().mockReturnValue(bescheidResource); + service.getResource = jest.fn().mockReturnValue(bescheidResource); buildCreateBescheidDocumentCommandPropsSpy = jest .spyOn(BescheidUtil, 'buildCreateBescheidDocumentCommandProps') .mockReturnValue(createCommandProps); @@ -1087,12 +1089,28 @@ describe('BescheidService', () => { }); describe('exists bescheid draft', () => { - it('should call resource service', () => { - service.bescheidDraftService.exists = jest.fn(); + beforeEach(() => { + service.bescheidDraftService.existResource = jest.fn().mockReturnValue(of(true)); + }); + it('should call bescheid draft service', () => { service.existsBescheidDraft(); - expect(service.bescheidDraftService.exists).toHaveBeenCalled(); + expect(service.bescheidDraftService.existResource).toHaveBeenCalled(); + }); + + it('should return false on missing resource', () => { + service.bescheidDraftService.existResource = jest.fn().mockReturnValue(of(false)); + + const exists: boolean = service.existsBescheidDraft(); + + expect(exists).toBeFalsy(); + }); + + it('should return true on existing resource', () => { + const exists: boolean = service.existsBescheidDraft(); + + expect(exists).toBeTruthy(); }); }); @@ -1103,14 +1121,14 @@ describe('BescheidService', () => { beforeEach(() => { service.deleteBescheid = jest.fn().mockReturnValue(of(commandStateResource)); service.deleteBescheidDocument = jest.fn(); + + service.getResource = jest.fn().mockReturnValue(createBescheidResource()); }); it('should get resource', () => { - service.bescheidDraftService.getResource = jest.fn(); - service.bescheidVerwerfen().pipe(first()).subscribe(); - expect(service.bescheidDraftService.getResource).toHaveBeenCalled(); + expect(service.getResource).toHaveBeenCalled(); }); it('should delete bescheid', (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 90dbfa4346..42b888cbfa 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -13,17 +13,17 @@ import { tapOnCommandSuccessfullyDone, } from '@alfa-client/command-shared'; import { + HttpError, + ResourceListService, + StateResource, createEmptyStateResource, createStateResource, filterIsLoadedOrHasError, getEmbeddedResources, hasStateResourceError, - HttpError, isLoaded, isNotEmpty, - ResourceListService, sortByGermanDateStr, - StateResource, } from '@alfa-client/tech-shared'; import { VorgangCommandService, @@ -33,16 +33,17 @@ import { } from '@alfa-client/vorgang-shared'; import { getEmpfaenger } from '@alfa-client/vorgang-shared-ui'; import { Injectable } from '@angular/core'; -import { getUrl, hasLink, ResourceUri } from '@ngxp/rest'; +import { ResourceUri, getUrl, hasLink } from '@ngxp/rest'; import { BehaviorSubject, + Observable, + Subscription, filter, first, map, - Observable, startWith, - Subscription, switchMap, + take, tap, } from 'rxjs'; import { @@ -223,7 +224,7 @@ export class BescheidService { } public updateBescheid(bescheid: Bescheid): Observable<StateResource<CommandResource>> { - return this.doUpdateBescheid(this.bescheidDraftService.getResource(), bescheid).pipe( + return this.doUpdateBescheid(this.getResource(), bescheid).pipe( tapOnCommandSuccessfullyDone((commandStateResource: StateResource<CommandResource>) => { this.updateBescheidDraft(commandStateResource.resource); this.clearCreateBescheidDocumentInProgress(); @@ -268,7 +269,7 @@ export class BescheidService { } private updateBescheidDraft(command: CommandResource): void { - this.bescheidDraftService.setResourceByUri(getEffectedResourceUrl(command)); + this.bescheidDraftService.loadByResourceUri(getEffectedResourceUrl(command)); } public getAttachments(): Observable<BinaryFileResource[]> { @@ -422,7 +423,7 @@ export class BescheidService { doCreateBescheidDocument(): Observable<StateResource<CommandResource>> { return this.commandService.createCommandByProps( - buildCreateBescheidDocumentCommandProps(this.bescheidDraftService.getResource()), + buildCreateBescheidDocumentCommandProps(this.getResource()), ); } @@ -453,11 +454,16 @@ export class BescheidService { } public existsBescheidDraft(): boolean { - return this.bescheidDraftService.exists(); + let exists: boolean; + this.bescheidDraftService + .existResource() + .pipe(take(1)) + .subscribe((existsDraft: boolean) => (exists = existsDraft)); + return exists; } public bescheidVerwerfen(): Observable<StateResource<CommandResource>> { - return this.deleteBescheid(this.bescheidDraftService.getResource()).pipe( + return this.deleteBescheid(this.getResource()).pipe( tapOnCommandSuccessfullyDone(() => { this.deleteBescheidDocument(); this.vorgangService.reloadCurrentVorgang(); @@ -469,6 +475,23 @@ export class BescheidService { return this.commandService.createCommandByProps(buildDeleteBescheidCommandProps(bescheid)); } + /** + * @returns @deprecated Don't use this function, instead passing data to function if necessarry. + */ + getResource(): BescheidResource { + let resource: StateResource<BescheidResource> = undefined; + const selected = this.bescheidDraftService.get().pipe( + filter((stateResource: StateResource<BescheidResource>) => !stateResource.loading), + take(1), + ); + const sub = selected.subscribe( + (stateResource: StateResource<BescheidResource>) => (resource = stateResource), + ); + sub.unsubscribe(); + + return resource.resource; + } + public reloadCurrentVorgang(): void { this.vorgangService.reloadCurrentVorgang(); } 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 0eb9f03aa4..9a564442fd 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 @@ -32,7 +32,6 @@ export class CommandResourceService<B extends Resource, T extends Resource> exte } public delete(): Observable<StateResource<CommandResource>> { - this.verifyDeleteLinkRel(); return this.commandService.createCommandByProps(this.buildDeleteCommandProps()); } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts index 1a742a30f0..8b559b8b81 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/api-resource.service.spec.ts @@ -46,14 +46,6 @@ describe('ApiResourceService', () => { const resourceWithEditLinkRel: Resource = createDummyResource([editLinkRel]); - it('should throw error if edit link not exists', () => { - service.stateResource.next(createStateResource(createDummyResource())); - - expect(() => service.save(dummyToSave)).toThrowError( - 'No edit link exists on current stateresource.', - ); - }); - it('should call repository', fakeAsync(() => { service.stateResource.next(createStateResource(resourceWithEditLinkRel)); repository.save.mockReturnValue(of(loadedResource)); @@ -99,37 +91,4 @@ describe('ApiResourceService', () => { expect(service.stateResource.value).toEqual(createStateResource(loadedResource)); })); }); - - describe('delete', () => { - const resourceWithDeleteLinkRel: Resource = createDummyResource([deleteLinkRel]); - const stateResourceWithDeleteLink: StateResource<Resource> = - createStateResource(resourceWithDeleteLinkRel); - - beforeEach(() => { - service.stateResource.next(stateResourceWithDeleteLink); - }); - - it('should throw error if delete linkRel not exists on current stateresource', () => { - service.stateResource.next(createStateResource(createDummyResource())); - - expect(() => service.delete()).toThrowError( - 'No delete link exists on current stateresource.', - ); - }); - - it('should call repository', () => { - service.delete(); - - expect(repository.delete).toHaveBeenCalledWith(resourceWithDeleteLinkRel, deleteLinkRel); - }); - - it('should return value', () => { - const deleteResource: Resource = createDummyResource(); - repository.delete.mockReturnValue(singleHot(deleteResource)); - - const deletedResource: Observable<Resource> = service.delete(); - - expect(deletedResource).toBeObservable(singleCold(deleteResource)); - }); - }); }); 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 2f328bcfd4..d19c4a6fdc 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 @@ -22,9 +22,4 @@ export class ApiResourceService<B extends Resource, T extends Resource> extends toSave, }); } - - public delete(): Observable<Resource> { - this.verifyDeleteLinkRel(); - return this.repository.delete(this.getResource(), this.config.delete.linkRel); - } } 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 8c6b5ae509..d61b71c6e5 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 @@ -11,6 +11,7 @@ import { createDummyResource } from '../../../test/resource'; import { HttpError, ProblemDetail } from '../tech.model'; import { LinkRelationName, ResourceServiceConfig } from './resource.model'; import { ResourceRepository } from './resource.repository'; +import { ResourceService } from './resource.service'; import { StateResource, createEmptyStateResource, @@ -18,7 +19,6 @@ import { createStateResource, } from './resource.util'; -import { ResourceService } from './resource.service'; import * as ResourceUtil from './resource.util'; describe('ResourceService', () => { @@ -31,6 +31,9 @@ describe('ResourceService', () => { const configStateResource: StateResource<Resource> = createStateResource(configResource); const configStateResource$: Observable<StateResource<Resource>> = of(configStateResource); + const dummyResource: Resource = createDummyResource(); + const dummyStateResource: StateResource<Resource> = createStateResource(dummyResource); + const editLinkRel: string = 'dummyEditLinkRel'; const getLinkRel: LinkRelationName = 'dummyGetLinkRel'; const deleteLinkRel: LinkRelationName = 'dummyDeleteLinkRel'; @@ -58,8 +61,7 @@ describe('ResourceService', () => { beforeEach(() => { service.stateResource.next(stateResource); - service.handleNullConfigResource = jest.fn(); - service.handleConfigResourceChanged = jest.fn(); + service.handleResourceChanges = jest.fn(); isInvalidResourceCombinationSpy = jest .spyOn(ResourceUtil, 'isInvalidResourceCombination') .mockReturnValue(true); @@ -69,17 +71,7 @@ describe('ResourceService', () => { service.get().subscribe(); tick(); - expect(service.handleConfigResourceChanged).toHaveBeenCalledWith( - stateResource, - configResource, - ); - })); - - it('should handle null configresource', fakeAsync(() => { - service.get().subscribe(); - tick(); - - expect(service.handleNullConfigResource).toHaveBeenCalledWith(configResource); + expect(service.handleResourceChanges).toHaveBeenCalledWith(stateResource, configResource); })); it('should call isInvalidResourceCombinationSpy', fakeAsync(() => { @@ -102,25 +94,21 @@ describe('ResourceService', () => { }); }); - describe('handle config resource changed', () => { + describe('handle resource changes', () => { const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); const changedConfigResource: Resource = createDummyResource(); describe('on different config resource', () => { - it('should update config resource if is different', () => { - service.configResource = createDummyResource(); - - service.handleConfigResourceChanged(stateResource, changedConfigResource); - - expect(service.configResource).toBe(changedConfigResource); + beforeEach(() => { + service.handleConfigResourceChanges = jest.fn(); }); - it('should set state resource reload', () => { + it('should update state resource by config resource', () => { service.configResource = createDummyResource(); - service.handleConfigResourceChanged(stateResource, changedConfigResource); + service.handleResourceChanges(stateResource, changedConfigResource); - expect(service.stateResource.value.reload).toBeTruthy(); + expect(service.handleConfigResourceChanges).toHaveBeenCalled(); }); }); @@ -134,7 +122,7 @@ describe('ResourceService', () => { it('should call shouldLoadResource', () => { service.shouldLoadResource = jest.fn(); - service.handleConfigResourceChanged(stateResource, configResource); + service.handleResourceChanges(stateResource, configResource); expect(service.shouldLoadResource).toHaveBeenCalledWith(stateResource, configResource); }); @@ -143,71 +131,146 @@ describe('ResourceService', () => { service.shouldLoadResource = jest.fn().mockReturnValue(true); service.loadResource = jest.fn(); - service.handleConfigResourceChanged(stateResource, configResource); + service.handleResourceChanges(stateResource, configResource); expect(service.loadResource).toHaveBeenCalledWith(configResource); }); - it('should NOT load resource on shouldLoadResource false', () => { + it('should NOT load resource', () => { service.loadResource = jest.fn(); service.shouldLoadResource = jest.fn().mockReturnValue(false); - service.handleConfigResourceChanged(stateResource, configResource); + service.handleResourceChanges(stateResource, configResource); expect(service.loadResource).not.toHaveBeenCalled(); }); }); }); - describe('handle null config resource', () => { - const resource: Resource = createDummyResource(); - const stateResource: StateResource<Resource> = createStateResource(resource); + describe('handle config resource changes', () => { + const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); - beforeEach(() => { - service.shouldClearStateResource = jest.fn(); - service.stateResource.next(stateResource); + describe('on stable stateresource', () => { + beforeEach(() => { + jest.spyOn(ResourceUtil, 'isStateResoureStable').mockReturnValue(true); + service.updateStateResourceByConfigResource = jest.fn(); + }); + + it('should update configresource', () => { + service.configResource = createDummyResource(); + + service.handleResourceChanges(stateResource, configResource); + + expect(service.configResource).toBe(configResource); + }); + + it('should update stateresource by configresource', () => { + service.handleResourceChanges(stateResource, configResource); + + expect(service.updateStateResourceByConfigResource).toHaveBeenCalledWith( + stateResource, + configResource, + ); + }); + }); + + describe('on instable stateresource', () => { + beforeEach(() => { + jest.spyOn(ResourceUtil, 'isStateResoureStable').mockReturnValue(false); + }); + + it('should NOT update configresource', () => { + const currentConfigResource = createDummyResource(); + service.configResource = currentConfigResource; + + service.handleResourceChanges(stateResource, configResource); + + expect(service.configResource).toBe(currentConfigResource); + }); + + it('should NOT update stateresource by configresource', () => { + service.updateStateResourceByConfigResource = jest.fn(); + + service.handleResourceChanges(stateResource, configResource); + + expect(service.updateStateResourceByConfigResource).not.toHaveBeenCalled(); + }); }); - it('should call shouldClearStateResource', () => { - service.handleNullConfigResource(null); + }); + + describe('update stateresource by configresource', () => { + it('should check if should clear stateresource', () => { + service.shouldClearStateResource = jest.fn(); + + service.updateStateResourceByConfigResource(dummyStateResource, configResource); - expect(service.shouldClearStateResource).toHaveBeenCalledWith(null); + expect(service.shouldClearStateResource).toHaveBeenCalled(); }); - it('should clear stateresource if shouldClearStateResource is true', () => { + it('should clear resource if should', () => { + service.stateResource.next(createStateResource(createDummyResource())); service.shouldClearStateResource = jest.fn().mockReturnValue(true); - service.handleNullConfigResource(null); + service.updateStateResourceByConfigResource(dummyStateResource, configResource); expect(service.stateResource.value).toEqual(createEmptyStateResource()); }); - it('should keep stateresource if shouldClearStateResource is false', () => { - service.shouldClearStateResource = jest.fn().mockReturnValue(false); + describe('on NOT clearing stateresource', () => { + beforeEach(() => { + service.shouldClearStateResource = jest.fn().mockReturnValue(false); + }); + + it('should load resource if link exists', () => { + service.hasGetLink = jest.fn(); - service.handleNullConfigResource(null); + service.updateStateResourceByConfigResource(dummyStateResource, configResource); - expect(service.stateResource.value).toBe(stateResource); + expect(service.hasGetLink).toHaveBeenCalledWith(configResource); + }); }); }); describe('should clear stateresource', () => { - const resource: Resource = createDummyResource(); - const stateResource: StateResource<Resource> = createStateResource(resource); + describe('on existing stateresource', () => { + beforeEach(() => { + service.stateResource.next(dummyStateResource); + }); - it('should return true on null configresource and filled stateresource', () => { - service.stateResource.next(stateResource); + it('should return true if configresource is null', () => { + const shouldClear: boolean = service.shouldClearStateResource(dummyStateResource, null); - const shouldClear: boolean = service.shouldClearStateResource(null); + expect(shouldClear).toBeTruthy(); + }); - expect(shouldClear).toBeTruthy(); + it('should return true if configresource has no get link', () => { + const shouldClear: boolean = service.shouldClearStateResource( + dummyStateResource, + createDummyResource(), + ); + + expect(shouldClear).toBeTruthy(); + }); }); - it('should return false on null configresource and empty stateresource', () => { - service.stateResource.next(createEmptyStateResource()); + describe('on empty stateresource', () => { + it('should return false', () => { + const shouldClear: boolean = service.shouldClearStateResource( + createEmptyStateResource(), + null, + ); - const shouldClear: boolean = service.shouldClearStateResource(null); + expect(shouldClear).toBeFalsy(); + }); + + it('should return false if configresource has no get link', () => { + const shouldClear: boolean = service.shouldClearStateResource( + createEmptyStateResource(), + createDummyResource(), + ); - expect(shouldClear).toBeFalsy(); + expect(shouldClear).toBeFalsy(); + }); }); }); @@ -247,12 +310,6 @@ describe('ResourceService', () => { describe('load resource', () => { const configResourceWithGetLinkRel: Resource = createDummyResource([getLinkRel]); - it('should throw error if getLinkRel not exists', () => { - expect(() => service.loadResource(configResource)).toThrowError( - 'No get link exists on configresource.', - ); - }); - it('should call do load resource', () => { service.doLoadResource = jest.fn(); @@ -314,12 +371,12 @@ describe('ResourceService', () => { }); }); - describe('setResourceByUri', () => { + describe('loadByResourceUri', () => { it('should do load resource', () => { service.doLoadResource = jest.fn(); const resourceUri: ResourceUri = faker.internet.url(); - service.setResourceByUri(resourceUri); + service.loadByResourceUri(resourceUri); expect(service.doLoadResource).toHaveBeenCalledWith(resourceUri); }); @@ -343,14 +400,6 @@ describe('ResourceService', () => { const resourceWithEditLinkRel: Resource = createDummyResource([editLinkRel]); - it('should throw error if edit link not exists', () => { - service.stateResource.next(createStateResource(createDummyResource())); - - expect(() => service.save(dummyToSave)).toThrowError( - 'No edit link exists on current stateresource.', - ); - }); - it('should do save', fakeAsync(() => { const stateResource: StateResource<Resource> = createStateResource(resourceWithEditLinkRel); service.stateResource.next(stateResource); @@ -434,77 +483,21 @@ describe('ResourceService', () => { }); }); - describe('can edit', () => { - it('should return true if link is present', () => { - const resource: StateResource<Resource> = createStateResource( - createDummyResource([editLinkRel]), - ); - service.stateResource.next(resource); - - const canEdit: boolean = service.canEdit(); - - expect(canEdit).toBeTruthy(); - }); - - it('should return false if link is NOT present', () => { - const resource: StateResource<Resource> = createStateResource(createDummyResource()); - service.stateResource.next(resource); - - const canEdit: boolean = service.canEdit(); - - expect(canEdit).toBeFalsy(); - }); - }); - - describe('can delete', () => { - it('should return true if link is present', () => { - const resource: StateResource<Resource> = createStateResource( - createDummyResource([deleteLinkRel]), - ); - service.stateResource.next(resource); - - const canEdit: boolean = service.canDelete(); - - expect(canEdit).toBeTruthy(); - }); - - it('should return false if link is NOT present', () => { - const resource: StateResource<Resource> = createStateResource(createDummyResource()); - service.stateResource.next(resource); - - const canEdit: boolean = service.canDelete(); - - expect(canEdit).toBeFalsy(); - }); - }); - - describe('get resource', () => { - it('should return resource from stateResource', () => { - const resource: Resource = createDummyResource(); - const stateResource: StateResource<Resource> = createStateResource(resource); - service.stateResource.next(stateResource); - - const result: Resource = service.getResource(); - - expect(result).toBe(resource); - }); - }); - - describe('exists', () => { - it('should return true', () => { - service.updateStateResource(createDummyResource()); + describe('exist resource', () => { + it('should return true on existing resource', () => { + service.stateResource.next(createStateResource(createDummyResource())); - const exists = service.exists(); + const existResource$: Observable<boolean> = service.existResource(); - expect(exists).toBeTruthy(); + expect(existResource$).toBeObservable(singleCold(true)); }); - it('should return false', () => { - service.updateStateResource(null); + it('should return false on null resource', () => { + service.stateResource.next(createEmptyStateResource()); - const exists = service.exists(); + const existResource$: Observable<boolean> = service.existResource(); - expect(exists).toBeFalsy(); + expect(existResource$).toBeObservable(singleCold(false)); }); }); }); 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 b87ef49e8d..8a05ace866 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 @@ -26,8 +26,8 @@ import { createStateResource, isInvalidResourceCombination, isLoadingRequired, + isStateResoureStable, StateResource, - throwErrorOn, } from './resource.util'; /** @@ -49,9 +49,8 @@ export abstract class ResourceService<B extends Resource, T extends Resource> { public get(): Observable<StateResource<T>> { return combineLatest([this.stateResource.asObservable(), this.getConfigResource()]).pipe( tap(([stateResource, configResource]) => - this.handleConfigResourceChanged(stateResource, configResource), + this.handleResourceChanges(stateResource, configResource), ), - tap(([, configResource]) => this.handleNullConfigResource(configResource)), filter( ([stateResource]) => !isInvalidResourceCombination(stateResource, this.configResource), ), @@ -70,33 +69,61 @@ export abstract class ResourceService<B extends Resource, T extends Resource> { ); } - handleConfigResourceChanged(stateResource: StateResource<T>, configResource: B): void { + handleResourceChanges(stateResource: StateResource<T>, configResource: B): void { if (!isEqual(this.configResource, configResource)) { - this.configResource = configResource; - this.stateResource.next({ ...this.stateResource.value, reload: true }); + this.handleConfigResourceChanges(stateResource, configResource); } else if (this.shouldLoadResource(stateResource, configResource)) { this.loadResource(configResource); } } + handleConfigResourceChanges(stateResource: StateResource<T>, configResource: B) { + if (isStateResoureStable(stateResource)) { + this.configResource = configResource; + this.updateStateResourceByConfigResource(stateResource, configResource); + } + } + + updateStateResourceByConfigResource(stateResource: StateResource<T>, configResource: B): void { + if (this.shouldClearStateResource(stateResource, configResource)) { + this.clearResource(); + } else if (this.hasGetLink(configResource)) { + this.loadResource(configResource); + } + } + + shouldClearStateResource(stateResource: StateResource<T>, configResource: B): boolean { + return ( + (isNull(configResource) || this.hasNotGetLink(configResource)) && + !this.isStateResourceEmpty(stateResource) + ); + } + + private hasNotGetLink(configResource: B): boolean { + return !this.hasGetLink(configResource); + } + + private isStateResourceEmpty(stateResource: StateResource<T>): boolean { + return isEqual(stateResource, createEmptyStateResource()); + } + + private clearResource(): void { + this.stateResource.next(createEmptyStateResource()); + } + + hasGetLink(configResource: B): boolean { + return isNotNull(configResource) && hasLink(configResource, this.config.getLinkRel); + } + shouldLoadResource(stateResource: StateResource<T>, configResource: B): boolean { return isNotNull(configResource) && isLoadingRequired(stateResource); } loadResource(configResource: B): void { - this.verifyGetLink(configResource); this.doLoadResource(getUrl(configResource, this.config.getLinkRel)); } - private verifyGetLink(configResource: B): void { - throwErrorOn( - !hasLink(configResource, this.config.getLinkRel), - 'No get link exists on configresource.', - ); - } - - //TODO rename to reloadByResourceUri - public setResourceByUri(resourceUri: ResourceUri): void { + public loadByResourceUri(resourceUri: ResourceUri): void { this.doLoadResource(resourceUri); } @@ -116,18 +143,7 @@ export abstract class ResourceService<B extends Resource, T extends Resource> { this.stateResource.next(createStateResource(resource)); } - handleNullConfigResource(configResource: B): void { - if (this.shouldClearStateResource(configResource)) { - this.stateResource.next(createEmptyStateResource()); - } - } - - shouldClearStateResource(configResource: B): boolean { - return isNull(configResource) && !isEqual(this.stateResource.value, createEmptyStateResource()); - } - public save(toSave: unknown): Observable<StateResource<T | HttpError>> { - this.verifyEditLinkRel(); const previousResource: T = this.stateResource.value.resource; return this.doSave(previousResource, toSave).pipe( tap((loadedResource: T) => this.stateResource.next(createStateResource(loadedResource))), @@ -143,52 +159,13 @@ export abstract class ResourceService<B extends Resource, T extends Resource> { return throwError(() => errorResponse); } - public verifyEditLinkRel(): void { - throwErrorOn( - !this.hasLinkRel(this.config.edit.linkRel), - 'No edit link exists on current stateresource.', - ); - } - abstract doSave(resource: T, toSave: unknown): Observable<T>; public refresh(): void { this.stateResource.next({ ...this.stateResource.value, reload: true }); } - /** - * @deprecated - */ - public canEdit(): boolean { - return this.hasLinkRel(this.config.edit.linkRel); - } - - protected hasLinkRel(linkRel: string): boolean { - return hasLink(this.getResource(), linkRel); - } - - /** - * @deprecated - */ - public getResource(): T { - return this.stateResource.value.resource; - } - - /** - * @deprecated - */ - public canDelete(): boolean { - return this.hasLinkRel(this.config.delete.linkRel); - } - - protected verifyDeleteLinkRel(): void { - throwErrorOn(!this.canDelete(), 'No delete link exists on current stateresource.'); - } - - /** - * @deprecated - */ - public exists(): boolean { - return isNotNull(this.stateResource.value.resource); + public existResource(): Observable<boolean> { + return this.stateResource.asObservable().pipe(mapToResource<T>(), map(isNotNull)); } } -- GitLab