diff --git a/alfa-client/libs/api-root-shared/src/lib/api-root.service.spec.ts b/alfa-client/libs/api-root-shared/src/lib/api-root.service.spec.ts index 6b31ca4e664c845ed921dfe0c99cb4edf266dbc9..3b6aec231b071a2237cb64bb74efbd83ce41e0cd 100644 --- a/alfa-client/libs/api-root-shared/src/lib/api-root.service.spec.ts +++ b/alfa-client/libs/api-root-shared/src/lib/api-root.service.spec.ts @@ -27,21 +27,23 @@ import { createStateResource, StateResource, } from '@alfa-client/tech-shared'; -import { mock } from '@alfa-client/test-utils'; +import { Mock, mock, useFromMock } from '@alfa-client/test-utils'; import { getUrl } from '@ngxp/rest'; -import { hot } from 'jest-marbles'; -import { of } from 'rxjs'; +import { cold, hot } from 'jest-marbles'; +import { Observable, of } from 'rxjs'; import { createBinaryFileResource } from '../../../binary-file-shared/test/binary-file'; -import { createApiRoot, createApiRootResource } from '../../test/api-root'; +import { createApiRootResource } from '../../test/api-root'; import { ApiRootFacade } from './+state/api-root.facade'; -import { ApiRoot, ApiRootResource } from './api-root.model'; +import { ApiRootResource } from './api-root.model'; import { ApiRootRepository } from './api-root.repository'; import { ApiRootService } from './api-root.service'; +import { singleCold } from '../../../tech-shared/src/lib/resource/marbles'; +import { fakeAsync, tick } from '@angular/core/testing'; describe('ApiRootService', () => { - var repository; - var service; - var facade; + let service: ApiRootService; + let repository: Mock<ApiRootRepository>; + let facade: Mock<ApiRootFacade>; const apiRoot: ApiRootResource = createApiRootResource(); const apiRootStateResource: StateResource<ApiRootResource> = createStateResource(apiRoot); @@ -49,58 +51,121 @@ describe('ApiRootService', () => { beforeEach(() => { repository = mock(ApiRootRepository); facade = mock(ApiRootFacade); - service = new ApiRootService(repository, facade); + + service = new ApiRootService(useFromMock(repository), useFromMock(facade)); }); - describe('calling getApiRoot', () => { + describe('get apiRoot', () => { beforeEach(() => { - repository.loadApiRoot.mockReturnValue(of(null)); + service.apiRootStateResource.next(apiRootStateResource); + service.handleApiRootChanges = jest.fn(); + service.shouldEmit = jest.fn().mockReturnValue(true); }); - it('should call repository', () => { - service.getApiRoot(); + it('should call handle apiRoot changes', fakeAsync(() => { + service.getApiRoot().subscribe(); + tick(); - expect(repository.loadApiRoot).toHaveBeenCalled(); + expect(service.handleApiRootChanges).toHaveBeenCalledWith(apiRootStateResource); + })); + + it('should call shouldEmit', fakeAsync(() => { + service.getApiRoot().subscribe(); + tick(); + + expect(service.shouldEmit).toHaveBeenCalledWith(apiRootStateResource); + })); + + it('should return value', () => { + service.apiRootStateResource.asObservable = jest + .fn() + .mockReturnValue(singleCold(apiRootStateResource, '-a')); + + const apiRootStateResource$: Observable<StateResource<ApiRootResource>> = + service.getApiRoot(); + + expect(apiRootStateResource$).toBeObservable( + cold('ab', { a: createEmptyStateResource(true), b: apiRootStateResource }), + ); }); + }); - it('should not call repository if already loading', () => { - service.apiRoot$.next(createEmptyStateResource(true)); + describe('handle apiRoot changes', () => { + it('should load apiRoot if loading required', () => { + service.loadApiRoot = jest.fn(); - service.getApiRoot(); + service.handleApiRootChanges(createEmptyStateResource()); - expect(repository.loadApiRoot).not.toHaveBeenCalled(); + expect(service.loadApiRoot).toHaveBeenCalled(); }); + it('should NOT load apiRoot if already loaded', () => { + service.loadApiRoot = jest.fn(); - it('should set loading flag', () => { - service.getApiRoot(); + service.handleApiRootChanges(apiRootStateResource); - expect(service.apiRoot$.value.loading).toBe(true); + expect(service.loadApiRoot).not.toHaveBeenCalled(); }); + }); - describe('on successful load', () => { - const apiRoot: ApiRoot = createApiRoot(); + describe('load apiRoot', () => { + beforeEach(() => { + service.apiRootStateResource.next(createEmptyStateResource()); + service.doLoadApiRoot = jest.fn(); + }); - beforeEach(() => { - repository.loadApiRoot.mockReturnValue(of(apiRoot)); - }); + it('should set apiRoot stateResource loading', () => { + service.loadApiRoot(); - it('should set loaded', () => { - service.getApiRoot(); + expect(service.apiRootStateResource.value.loading).toBeTruthy(); + }); - expect(service.apiRoot$.value.loaded).toBe(true); - }); + it('should do load apiRoot', () => { + service.loadApiRoot(); - it('should set resource', () => { - service.getApiRoot(); + expect(service.doLoadApiRoot).toHaveBeenCalled(); + }); + }); - expect(service.apiRoot$.value.resource).toBe(apiRoot); - }); + describe('do load apiRoot', () => { + const apiRoot: ApiRootResource = createApiRootResource(); - it('should unset loading', () => { - service.getApiRoot(); + beforeEach(() => { + service.apiRootStateResource.next(createEmptyStateResource()); + repository.loadApiRoot.mockReturnValue(of(apiRoot)); + }); + it('should call repository', () => { + service.doLoadApiRoot(); - expect(service.apiRoot$.value.loading).toBe(false); + expect(repository.loadApiRoot).toHaveBeenCalled(); + }); + + it('should update apiRoot stateResource', () => { + service.doLoadApiRoot(); + + expect(service.apiRootStateResource.value.resource).toBe(apiRoot); + }); + }); + + describe('should emit', () => { + it('should return true on loaded stateResource', () => { + const shouldEmit: boolean = service.shouldEmit(createStateResource(createApiRootResource())); + + expect(shouldEmit).toBeTruthy(); + }); + + it('should return true on loading stateResource', () => { + const shouldEmit: boolean = service.shouldEmit(createEmptyStateResource(true)); + + expect(shouldEmit).toBeTruthy(); + }); + + it('should return false on reload stateResource', () => { + const shouldEmit: boolean = service.shouldEmit({ + ...createStateResource(createApiRootResource()), + reload: true, }); + + expect(shouldEmit).toBeFalsy(); }); }); diff --git a/alfa-client/libs/api-root-shared/src/lib/api-root.service.ts b/alfa-client/libs/api-root-shared/src/lib/api-root.service.ts index e3dc4aa32f22eb19996b4f20b1fd32ebb84e9bc3..e6af890e2510944a0dde237518a76a098df012de 100644 --- a/alfa-client/libs/api-root-shared/src/lib/api-root.service.ts +++ b/alfa-client/libs/api-root-shared/src/lib/api-root.service.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { Injectable } from '@angular/core'; +import { Injectable, isDevMode } from '@angular/core'; import { createEmptyStateResource, createStateResource, @@ -31,16 +31,15 @@ import { } from '@alfa-client/tech-shared'; import { getUrl, Resource } from '@ngxp/rest'; import { BehaviorSubject, Observable } from 'rxjs'; -import { filter, switchMap } from 'rxjs/operators'; +import { filter, first, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiRootFacade } from './+state/api-root.facade'; import { ApiDownloadToken, ApiRootResource } from './api-root.model'; import { ApiRootRepository } from './api-root.repository'; @Injectable() export class ApiRootService { - private readonly apiRoot$: BehaviorSubject<StateResource<ApiRootResource>> = new BehaviorSubject< - StateResource<ApiRootResource> - >(createEmptyStateResource<ApiRootResource>()); + readonly apiRootStateResource: BehaviorSubject<StateResource<ApiRootResource>> = + new BehaviorSubject<StateResource<ApiRootResource>>(createEmptyStateResource()); constructor( private repository: ApiRootRepository, @@ -48,18 +47,42 @@ export class ApiRootService { ) {} public getApiRoot(): Observable<StateResource<ApiRootResource>> { - doIfLoadingRequired(this.apiRoot$.value, () => this.loadApiRoot()); - return this.apiRoot$.asObservable(); + return this.apiRootStateResource.asObservable().pipe( + tap((apiRootStateResource: StateResource<ApiRootResource>) => + this.handleApiRootChanges(apiRootStateResource), + ), + filter((apiRootStateResource: StateResource<ApiRootResource>) => + this.shouldEmit(apiRootStateResource), + ), + tap((apiRootStateResource: StateResource<ApiRootResource>) => { + if (isDevMode()) console.debug('APIROOT SERVICE - GET - EMIT: ', apiRootStateResource); + }), + startWith(createEmptyStateResource<ApiRootResource>(true)), + ); + } + + handleApiRootChanges(apiRootStateResource: StateResource<ApiRootResource>): void { + doIfLoadingRequired(apiRootStateResource, () => this.loadApiRoot()); } - private loadApiRoot(): void { - this.apiRoot$.next({ ...this.apiRoot$.value, loading: true }); + loadApiRoot(): void { + this.apiRootStateResource.next({ ...this.apiRootStateResource.value, loading: true }); + this.doLoadApiRoot(); + } - this.repository.loadApiRoot().subscribe((apiRoot) => { - if (apiRoot !== null) { - this.apiRoot$.next(createStateResource(apiRoot)); - } - }); + doLoadApiRoot(): void { + this.repository + .loadApiRoot() + .pipe(first()) + .subscribe((apiRoot: ApiRootResource) => + this.apiRootStateResource.next(createStateResource(apiRoot)), + ); + } + + shouldEmit(apiRootStateResource: StateResource<ApiRootResource>): boolean { + return ( + (apiRootStateResource.loaded || apiRootStateResource.loading) && !apiRootStateResource.reload + ); } //TOOD: In der Facade zur Verfuegung stellen, aufrufende Stelle anpassen und den service lib protected machen 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 30e7f2bbd5ef6d0e92736bb6cf3d567d48d5caaa..22aa4fbf7a5fb3411866f82b3feb2947fc246c93 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 @@ -7,6 +7,9 @@ import { buildCreateBescheidCommand } from './bescheid.util'; import { Observable, of } from 'rxjs'; import { createStateResource, StateResource } from '@alfa-client/tech-shared'; import { ResourceRepository } from '../../../tech-shared/src/lib/resource/resource.repository'; +import { singleCold } from '../../../tech-shared/src/lib/resource/marbles'; +import { BescheidResource } from './bescheid.model'; +import { createBescheidResource } from '../test/bescheid'; describe('BescheidService', () => { let service: BescheidService; @@ -22,7 +25,9 @@ describe('BescheidService', () => { facade = mock(BescheidFacade); vorgangService = mock(VorgangService); resourceRepository = mock(ResourceRepository); + vorgangService.getVorgangWithEingang.mockReturnValue(vorgangWithEingangStateResource$); + service = new BescheidService( useFromMock(facade), useFromMock(vorgangService), @@ -34,6 +39,31 @@ describe('BescheidService', () => { expect(service).toBeTruthy(); }); + describe('get bescheid draft', () => { + const bescheidDraft: BescheidResource = createBescheidResource(); + const bescheidDraftStateResource: StateResource<BescheidResource> = + createStateResource(bescheidDraft); + + it('should call resource service', () => { + service.resourceService.get = jest.fn(); + + service.getBescheidDraft(); + + expect(service.resourceService.get).toHaveBeenCalled(); + }); + + it('should return value', () => { + service.resourceService.get = jest + .fn() + .mockReturnValue(singleCold(bescheidDraftStateResource)); + + const bescheidStateResource$: Observable<StateResource<BescheidResource>> = + service.getBescheidDraft(); + + expect(bescheidStateResource$).toBeObservable(singleCold(bescheidDraftStateResource)); + }); + }); + describe('getBescheidCommand', () => { it('should call facade', () => { service.getBescheidCommand(); 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 369b6498990536510e3d806a4a2c39ae53992a8e..6da8f80a4acffff5bed60dd3395f2d9a031bed07 100644 --- a/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts +++ b/alfa-client/libs/bescheid-shared/src/lib/bescheid.service.ts @@ -1,5 +1,5 @@ import { CommandResource } from '@alfa-client/command-shared'; -import { createStateResource, StateResource } from '@alfa-client/tech-shared'; +import { StateResource } from '@alfa-client/tech-shared'; import { VorgangService, VorgangWithEingangLinkRel, @@ -36,20 +36,9 @@ export class BescheidService { } public getBescheidDraft(): Observable<StateResource<BescheidResource>> { - // temporary dummy resource - return of( - createStateResource<BescheidResource>({ - beschiedenAm: '2024-03-01', - bewilligt: true, - _links: { - self: { - href: '', - }, - }, - }), - ); - // return this.resourceService.get(); + return this.resourceService.get(); } + public getBescheidCommand(): Observable<StateResource<CommandResource>> { return this.facade.getBescheidCommand(); } diff --git a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.itcase.spec.ts b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.itcase.spec.ts index 61a6ad104301928ed2b0ca12b77851fba02115a1..0cde7b27c60bf0c2476cf724da8f0fae23f7844a 100644 --- a/alfa-client/libs/tech-shared/src/lib/resource/resource.service.itcase.spec.ts +++ b/alfa-client/libs/tech-shared/src/lib/resource/resource.service.itcase.spec.ts @@ -40,19 +40,16 @@ describe('ResourceService ITCase', () => { service = new ResourceService<Resource, Resource>(config, useFromMock(repository)); repository.getResource.mockReturnValueOnce(of(loadedResource)); - resetConfigStateResource(); + service.stateResource.next(createEmptyStateResource()); }); - function resetConfigStateResource(): void { - configResourceSubj.next(configStateResource); - } - describe('get', () => { it('should emit initially loading stateResource', (done) => { let emittedTimes: number = 0; service.get().subscribe((response: StateResource<Resource>) => { emittedTimes++; if (emittedTimes === 1) { + console.info('RESPONSE 1: ', response); expect(response.loading).toBeTruthy(); expect(response.resource).toBeNull(); done(); @@ -92,9 +89,9 @@ describe('ResourceService ITCase', () => { service.get().subscribe((response) => { emittedTimes++; - console.info('Respone: ', response); + console.info('RESPONSE ON GET: ', response); }); - + console.info('EMITTED TIMES: ', emittedTimes); expect(emittedTimes).toBe(EXPECTED_EMITTED_TIMES_FOR_GET); }); }); 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 e4ce9092383f014d88d3c852d998f2ecd61fd596..92e807edf17a80df5d90cc31e73f26a06bf97f47 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 @@ -18,6 +18,7 @@ import { createProblemDetail } from 'libs/tech-shared/test/error'; import { HttpError, ProblemDetail } from '../tech.model'; import * as ResourceUtil from './resource.util'; +import { cold } from 'jest-marbles'; describe('ResourceService', () => { let service: ResourceService<Resource, Resource>; @@ -58,7 +59,7 @@ describe('ResourceService', () => { service.handleNullConfigResource = jest.fn(); service.handleConfigResource = jest.fn(); service.handleConfigResourceChanged = jest.fn(); - service.shouldEmit = jest.fn(); + service.shouldFilter = jest.fn().mockReturnValue(true); }); it('should handle config resource changed', fakeAsync(() => { @@ -82,12 +83,24 @@ describe('ResourceService', () => { expect(service.handleConfigResource).toHaveBeenCalledWith(stateResource, configResource); })); - it('should call shouldEmit', fakeAsync(() => { + it('should call shouldFilter', fakeAsync(() => { service.get().subscribe(); tick(); - expect(service.shouldEmit).toHaveBeenCalled(); + expect(service.shouldFilter).toHaveBeenCalled(); })); + + it('should return initial value', () => { + service.stateResource.asObservable = jest + .fn() + .mockReturnValue(singleHot(stateResource, '-a')); + + const apiRootStateResource$: Observable<StateResource<Resource>> = service.get(); + + expect(apiRootStateResource$).toBeObservable( + cold('a', { a: createEmptyStateResource(true) }), + ); + }); }); describe('handle config resource changed', () => { @@ -108,14 +121,6 @@ describe('ResourceService', () => { expect(service.stateResource.value.reload).toBeTruthy(); }); - - it('should stop emittion', () => { - service.stopEmittion = jest.fn(); - - service.handleConfigResourceChanged(changedConfigResource); - - expect(service.stopEmittion).toHaveBeenCalled(); - }); }); describe('handle null config resource', () => { @@ -147,15 +152,6 @@ describe('ResourceService', () => { expect(service.stateResource.value).toBe(stateResource); }); - - it('should stop emittion', () => { - service.shouldClearStateResource = jest.fn().mockReturnValue(true); - service.stopEmittion = jest.fn(); - - service.handleNullConfigResource(null); - - expect(service.stopEmittion).toHaveBeenCalled(); - }); }); describe('should clear stateresource', () => { @@ -325,14 +321,77 @@ describe('ResourceService', () => { }); }); - describe('should emit', () => { + describe('should filter', () => { it('should return true if stateresource reload is true', () => { - const shouldFilter: boolean = service.shouldEmit({ + const shouldFilter: boolean = service.shouldFilter({ ...createStateResource(createDummyResource()), reload: true, }); - expect(shouldFilter).toBeFalsy(); + expect(shouldFilter).toBeTruthy(); + }); + + it('should return true on invalidCombination', () => { + service.isInvalidResourceCombination = jest.fn().mockReturnValue(true); + + const shouldFilter: boolean = service.shouldFilter( + createStateResource(createDummyResource()), + ); + + expect(shouldFilter).toBeTruthy(); + }); + + it('should call isInvalidCombination', () => { + const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); + service.configResource = configResource; + service.isInvalidResourceCombination = jest.fn(); + + service.shouldFilter(stateResource); + + expect(service.isInvalidResourceCombination).toHaveBeenCalledWith( + stateResource, + configResource, + ); + }); + }); + + describe('is invalid resource combination', () => { + it('should return true on loaded stateResource while configResource is null', () => { + const stateResource: StateResource<Resource> = createStateResource(createDummyResource()); + + const isInvalidCombination: boolean = service.isInvalidResourceCombination( + stateResource, + null, + ); + + expect(isInvalidCombination).toBeTruthy(); + }); + + it('should return true on non loaded stateResource while configResource exists', () => { + const stateResource: StateResource<Resource> = createEmptyStateResource(); + const configResource: Resource = createDummyResource(); + + const isInvalidCombination: boolean = service.isInvalidResourceCombination( + stateResource, + configResource, + ); + + expect(isInvalidCombination).toBeTruthy(); + }); + + it('should return false on loading stateResource', () => { + const stateResource: StateResource<Resource> = { + ...createStateResource(createDummyResource()), + loading: true, + }; + const configResource: Resource = createDummyResource(); + + const isInvalidCombination: boolean = service.isInvalidResourceCombination( + stateResource, + configResource, + ); + + expect(isInvalidCombination).toBeFalsy(); }); }); 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 77f0d263ad12b2a8d5e26b38ba2528f2c745e893..191966e2a8c7a4797e4b0f4b7008e7845f2a86e9 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,7 +1,6 @@ import { BehaviorSubject, Observable, - Subject, catchError, combineLatest, filter, @@ -9,7 +8,6 @@ import { mergeMap, of, startWith, - takeUntil, tap, throwError, } from 'rxjs'; @@ -29,60 +27,57 @@ import { isNotNull } from '../tech.util'; import { HttpErrorResponse } from '@angular/common/http'; import { isUnprocessableEntity } from '../http.util'; import { HttpError, ProblemDetail } from '../tech.model'; +import { isDevMode } from '@angular/core'; /** * B = Type of baseresource * T = Type of the resource which is working on */ export class ResourceService<B extends Resource, T extends Resource> { - stateResource: BehaviorSubject<StateResource<T>> = new BehaviorSubject( + readonly stateResource: BehaviorSubject<StateResource<T>> = new BehaviorSubject( createEmptyStateResource(), ); configResource: B; - configResourceChanged$ = new Subject(); constructor( private config: ResourceServiceConfig<B>, private repository: ResourceRepository, - ) { - this.config.resource.subscribe((configStateResource) => { - this.configResource = configStateResource.resource; - }); - } + ) {} public get(): Observable<StateResource<T>> { return combineLatest([this.stateResource.asObservable(), this.getConfigResource()]).pipe( tap(([, configResource]) => this.handleConfigResourceChanged(configResource)), tap(([, configResource]) => this.handleNullConfigResource(configResource)), - takeUntil(this.configResourceChanged$), tap(([stateResource, configResource]) => this.handleConfigResource(stateResource, configResource), ), - filter(([stateResource]) => this.shouldEmit(stateResource)), - map(([stateResource]) => { - return stateResource; + filter(([stateResource]) => !this.shouldFilter(stateResource)), + map(([stateResource]) => stateResource), + tap((stateResource: StateResource<T>) => { + if (isDevMode()) console.debug('RESOURCE SERVICE - GET - EMIT:', stateResource); }), startWith(createEmptyStateResource<T>(true)), ); } private getConfigResource(): Observable<B> { - return this.config.resource.pipe(map((stateResource) => stateResource.resource)); + return this.config.resource.pipe( + filter((stateResource) => stateResource.loaded && !stateResource.loading), + map((stateResource) => stateResource.resource), + ); } handleConfigResourceChanged(configResource: B): void { if (!isEqual(this.configResource, configResource)) { this.configResource = configResource; this.stateResource.next({ ...this.stateResource.value, reload: true }); - this.stopEmittion(); } } handleNullConfigResource(configResource: B): void { if (this.shouldClearStateResource(configResource)) { this.stateResource.next(createEmptyStateResource()); - this.stopEmittion(); } } @@ -90,10 +85,6 @@ export class ResourceService<B extends Resource, T extends Resource> { return isNull(configResource) && !isEqual(this.stateResource.value, createEmptyStateResource()); } - stopEmittion(): void { - this.configResourceChanged$.next(null); - } - handleConfigResource(stateResource: StateResource<T>, configResource: B): void { if (this.shouldLoadResource(stateResource, configResource)) { this.loadResource(configResource); @@ -125,7 +116,7 @@ export class ResourceService<B extends Resource, T extends Resource> { } setStateResourceLoading(): void { - this.stateResource.next({ ...this.stateResource.value, loading: true, reload: false }); + this.stateResource.next({ ...createEmptyStateResource(true), reload: false }); } doLoadResource(configResource: B): void { @@ -139,8 +130,21 @@ export class ResourceService<B extends Resource, T extends Resource> { this.stateResource.next(createStateResource(resource)); } - shouldEmit(stateResource: StateResource<Resource>): boolean { - return !stateResource.reload; + shouldFilter(stateResource: StateResource<Resource>): boolean { + return ( + stateResource.reload || this.isInvalidResourceCombination(stateResource, this.configResource) + ); + } + + isInvalidResourceCombination( + stateResource: StateResource<Resource>, + baseResource: Resource, + ): boolean { + return ( + !stateResource.loading && + ((!stateResource.loaded && isNotNull(baseResource)) || + (stateResource.loaded && isNull(baseResource))) + ); } public save(toSave: unknown): Observable<StateResource<T | HttpError>> { diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts index a60d700113e084589268d8cdc65e485646bdf1b1..4700c1864214713fca31d9720774f848195360de 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.spec.ts @@ -21,11 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { BescheidService } from '@alfa-client/bescheid-shared'; +import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; import { createEmptyStateResource, createStateResource, HasLinkPipe, + StateResource, } from '@alfa-client/tech-shared'; import { mock } from '@alfa-client/test-utils'; import { @@ -33,7 +34,11 @@ import { OzgcloudDialogService, OzgcloudStrokedButtonWithSpinnerComponent, } from '@alfa-client/ui'; -import { VorgangCommandService, VorgangWithEingangLinkRel } from '@alfa-client/vorgang-shared'; +import { + VorgangCommandService, + VorgangWithEingangLinkRel, + VorgangWithEingangResource, +} from '@alfa-client/vorgang-shared'; import { DialogRef } from '@angular/cdk/dialog'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; @@ -126,14 +131,14 @@ describe('BescheidenButtonComponent', () => { describe('onClick', () => { it('should call openBescheidenWizard', () => { - const openBescheidenWizard = (component.openBescheidenWizard = jest.fn()); + component.openBescheidenWizard = jest.fn(); component.vorgang = createVorgangWithEingangResource([ VorgangWithEingangLinkRel.CREATE_BESCHEID_DRAFT, ]); component.onClick(); - expect(openBescheidenWizard).toHaveBeenCalled(); + expect(component.openBescheidenWizard).toHaveBeenCalled(); }); it('should call vorgangCommandService.bescheiden', () => { @@ -173,21 +178,20 @@ describe('BescheidenButtonComponent', () => { component.vorgang = createVorgangWithEingangResource([ VorgangWithEingangLinkRel.BESCHEID_DRAFT, ]); - const openBescheidenDialogWithExistingDraft = - (component.openBescheidenDialogWithExistingDraft = jest.fn()); + component.openBescheidenDialogWithExistingDraft = jest.fn(); component.openBescheidenWizard(); - expect(openBescheidenDialogWithExistingDraft).toHaveBeenCalled(); + expect(component.openBescheidenDialogWithExistingDraft).toHaveBeenCalled(); }); it('should open bescheiden dialog with new draft', () => { component.vorgang = createVorgangWithEingangResource(); - const openBescheidDialogWithNewDraft = (component.openBescheidDialogWithNewDraft = jest.fn()); + component.openBescheidDialogWithNewDraft = jest.fn(); component.openBescheidenWizard(); - expect(openBescheidDialogWithNewDraft).toHaveBeenCalled(); + expect(component.openBescheidDialogWithNewDraft).toHaveBeenCalled(); }); }); @@ -195,7 +199,7 @@ describe('BescheidenButtonComponent', () => { it('should open wizard', () => { component.vorgang = createVorgangWithEingangResource(); - component.openBescheidenWizard(); + component.openBescheidDialogWithNewDraft(); expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith( VorgangDetailBescheidenComponent, @@ -208,24 +212,26 @@ describe('BescheidenButtonComponent', () => { }); describe('openBescheidenDialogWithExistingDraft', () => { - const bescheidDraftStateResource = createStateResource(createBescheidResource()); + const bescheidDraftResource: BescheidResource = createBescheidResource(); + const bescheidDraftStateResource: StateResource<BescheidResource> = + createStateResource(bescheidDraftResource); + const vorgangWithEingangResource: VorgangWithEingangResource = createVorgangWithEingangResource( + [VorgangWithEingangLinkRel.BESCHEID_DRAFT], + ); beforeEach(() => { - component.vorgang = createVorgangWithEingangResource([ - VorgangWithEingangLinkRel.BESCHEID_DRAFT, - ]); - + component.vorgang = vorgangWithEingangResource; bescheidService.getBescheidDraft.mockReturnValue(of(bescheidDraftStateResource)); }); it('should open wizard if bescheid draft loaded', () => { - component.openBescheidenWizard(); + component.openBescheidenDialogWithExistingDraft(); expect(ozgcloudDialogService.openWizard).toHaveBeenCalledWith( VorgangDetailBescheidenComponent, { - bescheidDraftResource: bescheidDraftStateResource.resource, - vorgangWithEingangResource: component.vorgang, + bescheidDraftResource, + vorgangWithEingangResource, }, ); }); @@ -233,7 +239,17 @@ describe('BescheidenButtonComponent', () => { it('should not open wizard if bescheid draft not loaded', () => { bescheidService.getBescheidDraft.mockReturnValue(of(createEmptyStateResource())); - component.openBescheidenWizard(); + component.openBescheidenDialogWithExistingDraft(); + + expect(ozgcloudDialogService.openWizard).not.toHaveBeenCalled(); + }); + + it('should not open wizard on loading bescheid draft', () => { + bescheidService.getBescheidDraft.mockReturnValue( + of({ ...createStateResource(createBescheidResource()), loading: true }), + ); + + component.openBescheidenDialogWithExistingDraft(); expect(ozgcloudDialogService.openWizard).not.toHaveBeenCalled(); }); diff --git a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts index e7faa08933b60abb77b0850b9dd0409bc460e746..64fa9e56df3b9959492711ef4388394decc6f936 100644 --- a/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts +++ b/alfa-client/libs/vorgang-detail/src/lib/buttons/bescheiden-button/bescheiden-button.component.ts @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { BescheidService } from '@alfa-client/bescheid-shared'; +import { BescheidResource, BescheidService } from '@alfa-client/bescheid-shared'; import { CommandResource } from '@alfa-client/command-shared'; import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { OzgcloudDialogService } from '@alfa-client/ui'; @@ -77,13 +77,16 @@ export class BescheidenButtonComponent implements OnInit { } } - openBescheidenDialogWithExistingDraft() { + openBescheidenDialogWithExistingDraft(): void { this.bescheidService .getBescheidDraft() .pipe( - filter((stateResource) => stateResource.loaded), + filter( + (stateResource: StateResource<BescheidResource>) => + stateResource.loaded && !stateResource.loading, + ), first(), - map((stateResource) => stateResource.resource), + map((stateResource: StateResource<BescheidResource>) => stateResource.resource), ) .subscribe((bescheidDraftResource) => { const dialogData: BescheidenDialogData = { @@ -97,7 +100,7 @@ export class BescheidenButtonComponent implements OnInit { }); } - openBescheidDialogWithNewDraft() { + openBescheidDialogWithNewDraft(): void { const dialogData: BescheidenDialogData = { bescheidDraftResource: null, vorgangWithEingangResource: this.vorgang, diff --git a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts index a22ea644a4e518739625cb6ab37156ba6855a210..4000162ce31ccf80e427f31d0d7ce52271fdacc4 100644 --- a/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts +++ b/alfa-client/libs/vorgang-shared/src/lib/vorgang.service.ts @@ -37,7 +37,7 @@ import { doIfLoadingRequired, isNotNull, } from '@alfa-client/tech-shared'; -import { Inject, Injectable } from '@angular/core'; +import { Inject, Injectable, isDevMode } from '@angular/core'; import { ResourceUri, getUrl, hasLink } from '@ngxp/rest'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; import { Observable, combineLatest } from 'rxjs'; @@ -72,6 +72,9 @@ export class VorgangService { ); }), map(([vorgangWithEingang]) => vorgangWithEingang), + tap((vorgangWithEingang: StateResource<VorgangWithEingangResource>) => { + if (isDevMode()) console.debug('VORGANG SERVICE - GET - EMIT: ', vorgangWithEingang); + }), startWith(createEmptyStateResource<VorgangWithEingangResource>(true)), ); }