diff --git a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.actions.ts b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.actions.ts index 17e4fecdc1e86f739b15ee15da1a79e242437e92..d86d23bb1816ec31f27af8ef1d79bcbc9cdf0bf2 100644 --- a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.actions.ts +++ b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.actions.ts @@ -24,21 +24,34 @@ import { ApiError, ApiErrorAction, TypedActionCreatorWithProps } from '@goofy-client/tech-shared'; import { createAction, props } from '@ngrx/store'; import { TypedAction } from '@ngrx/store/src/models'; -import { ResourceUri } from '@ngxp/rest'; +import { Resource, ResourceUri } from '@ngxp/rest'; +import { BinaryFileListResource } from '../binary-file.model'; export interface SaveBinaryFileAsPdfAction { fileData: Blob fileName: string } +export interface DownloadBinaryFileSuccessAction { } + export interface DownloadBinaryFileAsPdfAction { uri: ResourceUri fileName: string, successAction: () => DownloadBinaryFileSuccessAction & TypedAction<string>, - failureAction: (apiError: ApiError) => ApiErrorAction & TypedAction<string>, + failureAction: (apiError: ApiError) => ApiErrorAction & TypedAction<string> } -export interface DownloadBinaryFileSuccessAction { } - export const downloadPdf: TypedActionCreatorWithProps<DownloadBinaryFileAsPdfAction> = createAction('[BinaryFile] Download pdf file', props<DownloadBinaryFileAsPdfAction>()); -export const saveAsPdf: TypedActionCreatorWithProps<SaveBinaryFileAsPdfAction> = createAction('[BinaryFile/API] Save file as pdf', props<SaveBinaryFileAsPdfAction>()); \ No newline at end of file +export const saveAsPdf: TypedActionCreatorWithProps<SaveBinaryFileAsPdfAction> = createAction('[BinaryFile/API] Save file as pdf', props<SaveBinaryFileAsPdfAction>()); + +export interface LoadBinaryFileListSuccessProps { + binaryFileList: BinaryFileListResource +} +export interface LoadBinaryFileListProps { + resource: Resource, + linkRel: string, + successAction: (binaryFileList: BinaryFileListResource) => LoadBinaryFileListSuccessProps & TypedAction<string>, + failureAction: (apiError: ApiError) => ApiErrorAction & TypedAction<string> +} + +export const loadBinaryFileList: TypedActionCreatorWithProps<LoadBinaryFileListProps> = createAction('[BinaryFile] Load BinaryFile List', props<LoadBinaryFileListProps>()); \ No newline at end of file diff --git a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.spec.ts b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.spec.ts index ba67cb0ed95fc588a759de934810a4b6e65e6f0d..dab751237ea77678695a7c15f10d9515604f9a9b 100644 --- a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.spec.ts +++ b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.spec.ts @@ -29,14 +29,16 @@ import { provideMockActions } from '@ngrx/effects/testing'; import { Action, createAction, props } from '@ngrx/store'; import { TypedAction } from '@ngrx/store/src/models'; import { provideMockStore } from '@ngrx/store/testing'; -import { ResourceUri } from '@ngxp/rest'; +import { Resource, ResourceUri } from '@ngxp/rest'; import { hot } from 'jasmine-marbles'; import { cold } from 'jest-marbles'; import { ColdObservable } from 'jest-marbles/typings/src/rxjs/cold-observable'; +import { createBinaryFileListResource, createBinaryFileResource } from 'libs/binary-file-shared/test/binary-file'; import { createApiError } from 'libs/tech-shared/test/error'; import { Observable, of } from 'rxjs'; +import { BinaryFileListResource } from '../binary-file.model'; import { BinaryFileRepository } from '../binary-file.repository'; -import { DownloadBinaryFileAsPdfAction } from './binary-file.actions'; +import { DownloadBinaryFileAsPdfAction, LoadBinaryFileListProps, LoadBinaryFileListSuccessProps } from './binary-file.actions'; import { BinaryFileEffects } from './binary-file.effects'; import * as BinaryFileActions from './binary-file.actions'; @@ -45,7 +47,7 @@ describe('BinaryFileEffects', () => { let actions: Observable<Action>; let effects: BinaryFileEffects; - const binaryFileRepository: Mock<BinaryFileRepository> = mock(BinaryFileRepository); + const repository: Mock<BinaryFileRepository> = mock(BinaryFileRepository); beforeEach(() => { TestBed.configureTestingModule({ @@ -55,7 +57,7 @@ describe('BinaryFileEffects', () => { provideMockStore(), { provide: BinaryFileRepository, - useValue: binaryFileRepository + useValue: repository }, ], }); @@ -78,12 +80,12 @@ describe('BinaryFileEffects', () => { actions = hot('-a', { a: downloadPdfAction }); effects.downloadPdf$.subscribe(() => { - expect(binaryFileRepository.downloadPdf).toHaveBeenCalledWith(uri); + expect(repository.downloadPdf).toHaveBeenCalledWith(uri); }); }); it('should dispatch success action on no error', () => { - binaryFileRepository.downloadPdf.mockReturnValue(of(fileData)); + repository.downloadPdf.mockReturnValue(of(fileData)); actions = hot('-a', { a: downloadPdfAction }); @@ -98,7 +100,7 @@ describe('BinaryFileEffects', () => { const apiError: ApiError = createApiError() const error = { error: { error: apiError } }; const errorResponse = cold('-#', {}, error); - binaryFileRepository.downloadPdf = jest.fn(() => errorResponse); + repository.downloadPdf = jest.fn(() => errorResponse); actions = hot('-a', { a: downloadPdfAction }); @@ -122,4 +124,48 @@ describe('BinaryFileEffects', () => { }) }) }) + + describe('loadBinaryFileList', () => { + + const resource: Resource = createBinaryFileResource(); + const linkRel: string = faker.internet.url(); + + const loadSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = createAction('[Test Action] Download Success', props<LoadBinaryFileListSuccessProps>()); + const loadFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Test Action] Download Failure', props<ApiErrorAction>()); + + const loadBinaryFileListAction: LoadBinaryFileListProps & TypedAction<string> = BinaryFileActions.loadBinaryFileList({ resource, linkRel, successAction: (binaryFileList: BinaryFileListResource) => loadSuccess({ binaryFileList }), failureAction: (apiError: ApiError) => loadFailure({ apiError }) }); + + const binaryFileList: BinaryFileListResource = createBinaryFileListResource(); + + beforeEach(() => { + repository.getFiles.mockReturnValue(of(binaryFileList)); + }) + + it('should call repository', () => { + actions = of(loadBinaryFileListAction); + + effects.loadBinaryFileList$.subscribe(); + + expect(repository.getFiles).toHaveBeenCalledWith(resource, linkRel); + }); + + it('should dispatch success action on no error', () => { + actions = hot('-a', { a: loadBinaryFileListAction }); + + const expected: ColdObservable = cold('-b', { b: loadSuccess({ binaryFileList })}); + expect(effects.loadBinaryFileList$).toBeObservable(expected); + }); + + it.skip('should dispatch failure actions on error', () => { + const apiError: ApiError = createApiError() + const error = { error: { error: apiError } }; + const errorResponse = cold('-#', {}, error); + repository.getFiles = jest.fn(() => errorResponse); + + const expected: ColdObservable = cold('--b', { b: loadFailure({ apiError }) }); + actions = hot('-a', { a: loadBinaryFileListAction }); + + expect(effects.loadBinaryFileList$).toBeObservable(expected); + }) + }); }); diff --git a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts index 253532c5d797ea33746899e44415ffa9f4c7f812..fe1e2fb42b6efc854029a05cfd21a010a1dd4db6 100644 --- a/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts +++ b/goofy-client/libs/binary-file-shared/src/lib/+state/binary-file.effects.ts @@ -24,9 +24,10 @@ import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { of } from 'rxjs'; -import { catchError, mergeMap, switchMap, tap } from 'rxjs/operators'; +import { catchError, map, mergeMap, switchMap, tap } from 'rxjs/operators'; +import { BinaryFileListResource } from '../binary-file.model'; import { BinaryFileRepository } from '../binary-file.repository'; -import { DownloadBinaryFileAsPdfAction, SaveBinaryFileAsPdfAction } from './binary-file.actions'; +import { DownloadBinaryFileAsPdfAction, LoadBinaryFileListProps, SaveBinaryFileAsPdfAction } from './binary-file.actions'; import * as saveAs from 'file-saver'; import * as BinaryFileActions from './binary-file.actions'; @@ -34,26 +35,31 @@ import * as BinaryFileActions from './binary-file.actions'; @Injectable() export class BinaryFileEffects { - constructor(private readonly actions$: Actions, private readonly binaryFileRepository: BinaryFileRepository) { } + constructor(private readonly actions$: Actions, private readonly repository: BinaryFileRepository) { } - downloadPdf$ = createEffect(() => - this.actions$.pipe( - ofType(BinaryFileActions.downloadPdf), - switchMap((action: DownloadBinaryFileAsPdfAction) => this.binaryFileRepository.downloadPdf(action.uri).pipe( - mergeMap((fileData: Blob) => [BinaryFileActions.saveAsPdf({ fileData, fileName: action.fileName }), action.successAction()]), - catchError(error => of(action.failureAction(error.error))) - )) - ) - ); + downloadPdf$ = createEffect(() => this.actions$.pipe( + ofType(BinaryFileActions.downloadPdf), + switchMap((action: DownloadBinaryFileAsPdfAction) => this.repository.downloadPdf(action.uri).pipe( + mergeMap((fileData: Blob) => [BinaryFileActions.saveAsPdf({ fileData, fileName: action.fileName }), action.successAction()]), + catchError(error => of(action.failureAction(error.error))) + )) + )); - saveAsPdf$ = createEffect(() => - this.actions$.pipe( - ofType(BinaryFileActions.saveAsPdf), - tap((action: SaveBinaryFileAsPdfAction) => this.save(action.fileData, action.fileName)) - ), { dispatch: false }) + saveAsPdf$ = createEffect(() => this.actions$.pipe( + ofType(BinaryFileActions.saveAsPdf), + tap((action: SaveBinaryFileAsPdfAction) => this.save(action.fileData, action.fileName)) + ), { dispatch: false }); save(fileData: Blob, name: string): void { saveAs(fileData, name); } + + loadBinaryFileList$ = createEffect(() => this.actions$.pipe( + ofType(BinaryFileActions.loadBinaryFileList), + switchMap((props: LoadBinaryFileListProps) => this.repository.getFiles(props.resource, props.linkRel).pipe( + map((binaryFileList: BinaryFileListResource) => props.successAction(binaryFileList)), + catchError(error => of(props.failureAction(error.error))) + )) + )); } \ No newline at end of file diff --git a/goofy-client/libs/command-shared/src/lib/+state/command.actions.ts b/goofy-client/libs/command-shared/src/lib/+state/command.actions.ts index 6a6639f4ca2f354c87a8716e6ba4b84e11479def..a1322827af7fd4673b78e491fe35d700552a1661 100644 --- a/goofy-client/libs/command-shared/src/lib/+state/command.actions.ts +++ b/goofy-client/libs/command-shared/src/lib/+state/command.actions.ts @@ -21,7 +21,26 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { TypedActionCreator } from '@goofy-client/tech-shared'; -import { createAction } from '@ngrx/store'; +import { ApiError, ApiErrorAction, StateResource, TypedActionCreator, TypedActionCreatorWithProps } from '@goofy-client/tech-shared'; +import { createAction, props } from '@ngrx/store'; +import { TypedAction } from '@ngrx/store/src/models'; +import { Resource } from '@ngxp/rest'; +import { CommandListResource, CommandResource } from '../command.model'; -export const publishConcurrentModificationAction: TypedActionCreator = createAction('[Command/API] Concurrent Modification') +export const publishConcurrentModificationAction: TypedActionCreator = createAction('[Command/API] Concurrent Modification'); + +export interface CommandStateResourceProps { + commandStateResource: StateResource<CommandResource> +} + +export interface LoadCommandListSuccessProps { + commandList: CommandListResource +} +export interface LoadCommandListProps { + resource: Resource, + linkRel: string, + successAction: (commandList: CommandListResource) => LoadCommandListSuccessProps & TypedAction<string>, + failureAction: (apiError: ApiError) => ApiErrorAction & TypedAction<string> +} + +export const loadCommandList: TypedActionCreatorWithProps<LoadCommandListProps> = createAction('[Command] Load pending commands', props<LoadCommandListProps>()); \ No newline at end of file diff --git a/goofy-client/libs/command-shared/src/lib/+state/command.effects.spec.ts b/goofy-client/libs/command-shared/src/lib/+state/command.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..20c91fbf6043ff3647addfe6d3d3a26939e21389 --- /dev/null +++ b/goofy-client/libs/command-shared/src/lib/+state/command.effects.spec.ts @@ -0,0 +1,87 @@ +import { TestBed } from '@angular/core/testing'; +import { ApiError, ApiErrorAction, TypedActionCreatorWithProps } from '@goofy-client/tech-shared'; +import { Mock, mock } from '@goofy-client/test-utils'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Action, createAction, props } from '@ngrx/store'; +import { TypedAction } from '@ngrx/store/src/models'; +import { provideMockStore } from '@ngrx/store/testing'; +import { Resource } from '@ngxp/rest'; +import { cold, hot } from 'jest-marbles'; +import { ColdObservable } from 'jest-marbles/typings/src/rxjs/cold-observable'; +import { createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; +import { createApiError } from 'libs/tech-shared/test/error'; +import { Observable, of } from 'rxjs'; +import { CommandListResource } from '../command.model'; +import { CommandRepository } from '../command.repository'; +import { LoadCommandListSuccessProps } from './command.actions'; +import { CommandEffects } from './command.effects'; + +import * as CommandActions from './command.actions'; + +describe('CommandEffects', () => { + let actions: Observable<Action>; + let effects: CommandEffects; + + const repository: Mock<CommandRepository> = mock(CommandRepository); + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CommandEffects, + provideMockActions(() => actions), + provideMockStore(), + { + provide: CommandRepository, + useValue: repository + }, + ], + }); + + effects = TestBed.inject(CommandEffects); + }); + + describe('loadCommandList', () => { + + const resource: Resource = createCommandResource(); + const linkRel: string = 'LinkRelationOfResource'; + + const loadSuccess: TypedActionCreatorWithProps<LoadCommandListSuccessProps> = createAction('[Test Action] Load Success', props<LoadCommandListSuccessProps>()); + const loadFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Test Action] Load Failure', props<ApiErrorAction>()); + + const loadCommandList: TypedAction<string> = CommandActions.loadCommandList({ resource, linkRel, successAction: (commandList) => loadSuccess({ commandList }), failureAction: (apiError) => loadFailure({ apiError })}); + + const commandListResource: CommandListResource = createCommandListResource(); + + beforeEach(() => { + repository.getPendingCommands.mockReturnValue(of(commandListResource)); + }) + + it('should call repository', () => { + actions = hot('-a', { a: loadCommandList }); + + effects.loadCommandList$.subscribe(() => { + expect(repository.getPendingCommands).toHaveBeenCalledWith(resource, linkRel); + }); + }); + + it('should dispatch success action on no error', () => { + actions = hot('-a', { a: loadCommandList }); + + const expected: ColdObservable = cold('-b', { b: loadSuccess({ commandList: commandListResource })}); + + expect(effects.loadCommandList$).toBeObservable(expected); + }); + + it('should dispatch failure action on error', () => { + const apiError: ApiError = createApiError() + const error = { error: apiError }; + const errorResponse = cold('-#', {}, error); + repository.getPendingCommands = jest.fn(() => errorResponse); + + actions = hot('-a', { a: loadCommandList }); + + const expected: ColdObservable = cold('--b', { b: loadFailure({ apiError }) }); + expect(effects.loadCommandList$).toBeObservable(expected); + }) + }); +}) \ No newline at end of file diff --git a/goofy-client/libs/command-shared/src/lib/+state/command.effects.ts b/goofy-client/libs/command-shared/src/lib/+state/command.effects.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7d9f38f555787fc7bba96c783685d77a4e41cfa --- /dev/null +++ b/goofy-client/libs/command-shared/src/lib/+state/command.effects.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { of } from 'rxjs'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import { CommandListResource } from '../command.model'; +import { CommandRepository } from '../command.repository'; +import { LoadCommandListProps, loadCommandList } from './command.actions'; + +@Injectable() +export class CommandEffects { + + constructor(private readonly actions$: Actions, private readonly repository: CommandRepository) { } + + loadCommandList$ = createEffect(() => this.actions$.pipe( + ofType(loadCommandList), + switchMap((props: LoadCommandListProps) => this.repository.getPendingCommands(props.resource, props.linkRel).pipe( + map((commandList: CommandListResource) => props.successAction(commandList)), + //FIXME + catchError(error => { + const returnObs = of(props.failureAction(error.error)); + return returnObs; + }) + )) + )); +} \ No newline at end of file diff --git a/goofy-client/libs/command-shared/src/lib/command-shared.module.ts b/goofy-client/libs/command-shared/src/lib/command-shared.module.ts index 89639fd5168e82cf3338febd50eaae11404002d4..eafd5c8c26c0a7f5e1b3a272415a6c1b5af6f2b3 100644 --- a/goofy-client/libs/command-shared/src/lib/command-shared.module.ts +++ b/goofy-client/libs/command-shared/src/lib/command-shared.module.ts @@ -23,11 +23,15 @@ */ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; +import { CommandEffects } from './+state/command.effects'; import { CommandFacade } from './+state/command.facade'; - @NgModule({ - imports: [CommonModule], + imports: [ + CommonModule, + EffectsModule.forFeature([CommandEffects]), + ], providers:[CommandFacade] }) export class CommandSharedModule { } diff --git a/goofy-client/libs/command-shared/src/lib/command.repository.ts b/goofy-client/libs/command-shared/src/lib/command.repository.ts index 091bcfad77ebdff049143d8eaba1e2a26d9b65c2..60b918b6fd9e6fc776294964fb50ba38cb2b6b56 100644 --- a/goofy-client/libs/command-shared/src/lib/command.repository.ts +++ b/goofy-client/libs/command-shared/src/lib/command.repository.ts @@ -41,7 +41,7 @@ export class CommandRepository { } public getPendingCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { - return this.resourceFactory.from(resource).get(linkRel) + return this.resourceFactory.from(resource).get(linkRel); } public revokeCommand(resource: CommandResource): Observable<CommandResource> { diff --git a/goofy-client/libs/command-shared/src/lib/command.service.spec.ts b/goofy-client/libs/command-shared/src/lib/command.service.spec.ts index 5f6cca74a8a9e4404bab21b1911deaeaeedc80a9..8a30ddf142099b565c9d29b59bb62a4edc7cd881 100644 --- a/goofy-client/libs/command-shared/src/lib/command.service.spec.ts +++ b/goofy-client/libs/command-shared/src/lib/command.service.spec.ts @@ -23,19 +23,19 @@ */ import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { faker } from '@faker-js/faker'; -import { createEmptyStateResource, createErrorStateResource, createStateResource, StateResource } from '@goofy-client/tech-shared'; -import { mock, Mock, useFromMock } from '@goofy-client/test-utils'; +import { StateResource, createEmptyStateResource, createErrorStateResource, createStateResource } from '@goofy-client/tech-shared'; +import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; import { SnackBarService } from '@goofy-client/ui'; import { Resource } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; -import { createCommand, createCommandErrorResource, createCommandResource } from 'libs/command-shared/test/command'; +import { createCommand, createCommandErrorResource, createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; import { createHttpErrorResponse } from 'libs/tech-shared/test/http'; import { toResource } from 'libs/tech-shared/test/resource'; import { Observable, of, throwError } from 'rxjs'; import { CommandFacade } from './+state/command.facade'; import { CommandLinkRel } from './command.linkrel'; import { CommandErrorMessage } from './command.message'; -import { Command, CommandResource } from './command.model'; +import { Command, CommandListResource, CommandResource } from './command.model'; import { CommandRepository } from './command.repository'; import { CommandService, IntervallHandleWithTickObservable, startInterval } from './command.service'; @@ -287,10 +287,10 @@ describe('CommandService', () => { describe('get pending commands', () => { - const listResource = {}; + const commandListResource: CommandListResource = createCommandListResource(); beforeEach(() => { - repository.getPendingCommands.mockReturnValue(cold('a', { a: listResource })); + repository.getPendingCommands.mockReturnValue(cold('a', { a: commandListResource })); }) it('should call repository', () => { diff --git a/goofy-client/libs/command-shared/src/lib/command.service.ts b/goofy-client/libs/command-shared/src/lib/command.service.ts index 933d73bf7c36b39258843e120b3e1f6f2e29f6e0..bd4f9c19edba6f97f157182e6758a67c5aa1f307 100644 --- a/goofy-client/libs/command-shared/src/lib/command.service.ts +++ b/goofy-client/libs/command-shared/src/lib/command.service.ts @@ -43,7 +43,7 @@ export class CommandService { public createCommand(resource: Resource, linkRel: string, command: CreateCommand): Observable<StateResource<CommandResource>> { return this.handleCommandResponse(this.repository.createCommand(resource, linkRel, command)); - } + } public revokeCommand(resource: CommandResource): Observable<StateResource<CommandResource>> { return this.handleCommandResponse(this.repository.revokeCommand(resource)); @@ -110,8 +110,8 @@ export class CommandService { window.clearInterval(handler); } - public getPendingCommands(resource: Resource, linkRel: string): Observable<StateResource<CommandListResource>> { - return this.repository.getPendingCommands(resource, linkRel).pipe(map(res => createStateResource(res))); + public getPendingCommands(resource: Resource, linkRel: string): Observable<CommandListResource> { + return this.repository.getPendingCommands(resource, linkRel); } public getEffectedResource<T>(command: CommandResource): Observable<T> { diff --git a/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.spec.ts b/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.spec.ts index 1cce43a88103bd6014f80d53ac977b6fc358bc3e..6d48e0bfffbfbb77403f8da4cb487e8ea829f5c5 100644 --- a/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.spec.ts +++ b/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.spec.ts @@ -23,7 +23,7 @@ */ import { CommandResource, CommandService } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; -import { createEmptyStateResource, createErrorStateResource, createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { StateResource, createEmptyStateResource, createErrorStateResource, createStateResource } from '@goofy-client/tech-shared'; import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; import { CreateForwardCommand, ForwardRequest, VorgangOrder, VorgangResource, VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@goofy-client/vorgang-shared'; import { cold, hot } from 'jest-marbles'; @@ -167,19 +167,6 @@ describe('ForwardingService', () => { }) }) - describe('clear forward command', () => { - - beforeEach(() => { - service.unsubscribe = jest.fn(); - }) - - it('should call vorgang service clear pending forward command', () => { - service.clearForwardCommand(); - - expect(vorgangService.clearPendingForwardCommand).toHaveBeenCalled(); - }) - }) - describe('onNavigation', () => { beforeEach(() => { diff --git a/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.ts b/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.ts index 73e9d838200fb5d7e114358ae4aaa311315fd905..e2566227f6a176b15f43b63f16fdba11f72417cd 100644 --- a/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.ts +++ b/goofy-client/libs/forwarding-shared/src/lib/forwarding.service.ts @@ -25,8 +25,8 @@ import { Injectable, OnDestroy } from '@angular/core'; import { Params } from '@angular/router'; import { CommandResource, CommandService, CreateCommand, isDone, isPending } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; -import { createEmptyStateResource, createStateResource, hasError, StateResource } from '@goofy-client/tech-shared'; -import { createForwardCommand, ForwardRequest, VorgangResource, VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource } from '@goofy-client/vorgang-shared'; +import { StateResource, createEmptyStateResource, createStateResource, hasError } from '@goofy-client/tech-shared'; +import { ForwardRequest, VorgangResource, VorgangService, VorgangWithEingangLinkRel, VorgangWithEingangResource, createForwardCommand } from '@goofy-client/vorgang-shared'; import { isNil } from 'lodash-es'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { first, map, startWith, tap } from 'rxjs/operators'; @@ -96,10 +96,6 @@ export class ForwardingService implements OnDestroy { return command.loaded && isPending(command.resource); } - clearForwardCommand(): void { - this.vorgangService.clearPendingForwardCommand(); - } - public forward(vorgang: VorgangResource, request: ForwardRequest): Observable<StateResource<CommandResource>> { this.vorgangService.setPendingForwardSingleCommandLoading(); const subscription: Subscription = this.commandService.createCommand(vorgang, VorgangWithEingangLinkRel.FORWARD, createForwardCommand(request)).subscribe(command => { diff --git a/goofy-client/libs/tech-shared/src/lib/ngrx/actions.ts b/goofy-client/libs/tech-shared/src/lib/ngrx/actions.ts index 8facd23201309a9e1992a82c30651568ff06a556..4d5404e557b641c35162419dc04ea62df8fdfd94 100644 --- a/goofy-client/libs/tech-shared/src/lib/ngrx/actions.ts +++ b/goofy-client/libs/tech-shared/src/lib/ngrx/actions.ts @@ -23,11 +23,16 @@ */ import { ActionCreator } from '@ngrx/store'; import { TypedAction } from '@ngrx/store/src/models'; +import { ResourceUri } from '@ngxp/rest'; import { ApiError } from '../tech.model'; export interface TypedActionCreatorWithProps<T> extends ActionCreator<string, (props: T) => T & TypedAction<string>> { } export interface TypedActionCreator extends ActionCreator<string, () => TypedAction<string>> { } -export interface ApiErrorAction { +export interface ApiErrorAction {//TODO rename ApiErrorProps apiError: ApiError +} + +export interface ResourceUriProps { + resourceUri: ResourceUri } \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts index 05d303e591a49de2bd1c9e0f3dbed39db03375e5..df317d501cd7e09ea3236bf62935d36720f9be29 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.actions.ts @@ -23,7 +23,9 @@ */ import { HttpErrorResponse } from '@angular/common/http'; import { ApiRootResource } from '@goofy-client/api-root-shared'; -import { ApiError, TypedActionCreator, TypedActionCreatorWithProps } from '@goofy-client/tech-shared'; +import { LoadBinaryFileListSuccessProps } from '@goofy-client/binary-file-shared'; +import { CommandResource, CommandStateResourceProps, LoadCommandListSuccessProps } from '@goofy-client/command-shared'; +import { ApiErrorAction, ResourceUriProps, StateResource, TypedActionCreator, TypedActionCreatorWithProps } from '@goofy-client/tech-shared'; import { createAction, props } from '@ngrx/store'; import { VorgangListResource, VorgangWithEingangResource } from '../vorgang.model'; @@ -46,9 +48,6 @@ export interface ApiRootWithLinkRelProps { linkRel: string } -export interface ApiErrorAction { - apiError: ApiError -} export interface HttpErrorAction { httpErrorResponse: HttpErrorResponse } @@ -75,6 +74,7 @@ export const searchVorgaengeByFailure: TypedActionCreatorWithProps<HttpErrorActi export const loadNextPage: TypedActionCreator = createAction('[Vorgang] Load next VorgangList page'); export const loadNextPageSuccess: TypedActionCreatorWithProps<VorgangListAction> = createAction('[Vorgang] Load next VorgangList page Success', props<VorgangListAction>()); +//Search export const searchForPreview: TypedActionCreatorWithProps<StringBasedProps> = createAction('[Vorgang] Search for preview', props<StringBasedProps>()); export const searchForPreviewSuccess: TypedActionCreatorWithProps<VorgangListAction> = createAction('[Vorgang] Search for preview Success', props<VorgangListAction>()); export const searchForPreviewFailure: TypedActionCreatorWithProps<HttpErrorAction> = createAction('[Vorgang] Search for preview Failure', props<HttpErrorAction>()); @@ -85,7 +85,35 @@ export const clearSearchString: TypedActionCreator = createAction('[Vorgang] Cle export const setSearchString: TypedActionCreatorWithProps<StringBasedProps> = createAction('[Vorgang] Set search string', props<StringBasedProps>()); //VorgangWithEingang -export const loadVorgangWithEingang: TypedActionCreator = createAction('[Vorgang] Load VorgangWithEingang'); +export const loadVorgangWithEingang: TypedActionCreatorWithProps<ResourceUriProps> = createAction('[Vorgang] Load VorgangWithEingang', props<ResourceUriProps>()); export const loadVorgangWithEingangSuccess: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction('[Vorgang] Load VorgangWithEingang Success', props<VorgangWithEingangAction>()); +export const loadVorgangWithEingangFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Vorgang] Load VorgangWithEingang Failure', props<ApiErrorAction>()); + +export const clearVorgangWithEingang: TypedActionCreator = createAction('[Vorgang] Clear VorgangWithEingang'); + +export const loadPendingCommandList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction('[Vorgang] Load pending command list', props<VorgangWithEingangAction>()); +export const loadPendingCommandListSuccess: TypedActionCreatorWithProps<LoadCommandListSuccessProps> = createAction('[Vorgang/API] Load pending command list Success', props<LoadCommandListSuccessProps>()); +export const loadPendingCommandListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Vorgang/API] Load pending command list Failure', props<ApiErrorAction>()); + +export const loadAttachmentList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction('[Vorgang] Load AttachmentList', props<VorgangWithEingangAction>()); +export const loadAttachmentListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = createAction('[Vorgang] Load AttachmentList Success', props<LoadBinaryFileListSuccessProps>()); +export const loadAttachmentListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Vorgang] Load AttachmentList Failure', props<ApiErrorAction>()); + +export const loadRepresentationList: TypedActionCreatorWithProps<VorgangWithEingangAction> = createAction('[Vorgang] Load RepresentationList', props<VorgangWithEingangAction>()); +export const loadRepresentationListSuccess: TypedActionCreatorWithProps<LoadBinaryFileListSuccessProps> = createAction('[Vorgang] Load RepresentationList Success', props<LoadBinaryFileListSuccessProps>()); +export const loadRepresentationListFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Vorgang] Load RepresentationList Failure', props<ApiErrorAction>()); + +export const assignUser: TypedActionCreator = createAction('[Vorgang] Assign User'); + +export const setForwardingSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = createAction('[Vorgang] Set forward command', props<CommandStateResourceProps>()); +export const setForwardingSingleCommandLoading: TypedActionCreator = createAction('[Vorgang] Set forward command loading'); +export const setSendPostfachNachrichtSingleCommand: TypedActionCreatorWithProps<CommandStateResourceProps> = createAction('[Vorgang] Set send postfach nachricht command', props<CommandStateResourceProps>()); +export const setSendPostfachNachrichtSingleCommandLoading: TypedActionCreator = createAction('[Vorgang] Set send postfach nachricht command loading'); +export interface SetAssignUserProps { + commandStateResource: StateResource<CommandResource>; +} +export const setAssignUser: TypedActionCreatorWithProps<SetAssignUserProps> = createAction('[Vorgang] Set Assign User', props<SetAssignUserProps>()); + +export const assignUserFailure: TypedActionCreatorWithProps<ApiErrorAction> = createAction('[Vorgang] Assign User Failure', props<ApiErrorAction>()); export const setReloadVorgangWithEingang: TypedActionCreator = createAction('[Vorgang] Set reload at VorgangWithEingang'); diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.spec.ts index 4ac8c501eeeb91f9209a05c4574951e287e53c1d..759cb510b12d5a3eb03bcc01914a8236557be2e0 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.spec.ts @@ -24,22 +24,28 @@ import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; import { ApiRootFacade, ApiRootLinkRel, ApiRootResource } from '@goofy-client/api-root-shared'; +import { LoadBinaryFileListProps, loadBinaryFileList } from '@goofy-client/binary-file-shared'; +import { LoadCommandListProps, loadCommandList } from '@goofy-client/command-shared'; import { ApiError, createStateResource } from '@goofy-client/tech-shared'; import { mock } from '@goofy-client/test-utils'; import { SnackBarService } from '@goofy-client/ui'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { ResourceUri } from '@ngxp/rest'; import { cold, hot } from 'jest-marbles'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { createApiError, createHttpErrorResponse } from 'libs/tech-shared/test/error'; -import { createVorgangListResource } from 'libs/vorgang-shared/test/vorgang'; +import { createVorgangListResource, createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { Observable, of } from 'rxjs'; -import { VorgangFilter, VorgangListResource } from '../vorgang.model'; +import { VorgangWithEingangLinkRel } from '../vorgang.linkrel'; +import { VorgangFilter, VorgangListResource, VorgangWithEingangResource } from '../vorgang.model'; import { VorgangEffects } from './vorgang.effects'; import { VorgangFacade } from './vorgang.facade'; import { VorgangRepository } from './vorgang.repository'; +import faker from '@faker-js/faker'; + import * as VorgangActions from './vorgang.actions'; import * as VorgangSelectors from './vorgang.selectors'; @@ -54,6 +60,7 @@ describe('VorgangEffects', () => { const snackbarService = mock(SnackBarService); const vorgangList: VorgangListResource = createVorgangListResource(); + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); beforeEach(() => { TestBed.configureTestingModule({ @@ -125,7 +132,6 @@ describe('VorgangEffects', () => { describe('searchVorgaengeBy', () => { - const vorgangList: VorgangListResource = createVorgangListResource(); const apiRoot: ApiRootResource = createApiRootResource(); const searchString: string = 'search like me'; const linkRel: string = 'linkRelationName'; @@ -291,4 +297,206 @@ describe('VorgangEffects', () => { expect(snackbarService.showError).toHaveBeenCalled(); }) }) + + //VorgangWithEingang + describe('loadVorgangWithEingang', () => { + + const resourceUri: ResourceUri = faker.internet.url(); + const action = VorgangActions.loadVorgangWithEingang({ resourceUri }); + + it('should dispatch success action', () => { + vorgangRepository.getVorgang.mockReturnValue(of(vorgangWithEingang)); + + actions = hot('-a-|', { a: action }); + + const expected = hot('-a-|', { a: VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang }) }); + expect(effects.loadVorgangWithEingang$).toBeObservable(expected); + }) + + it('should dispatch failure action', () => { + const apiError: ApiError = createApiError() + const error = { error: { error: apiError } }; + const errorResponse = cold('-#', {}, error); + vorgangRepository.getVorgang = jest.fn(() => errorResponse); + + const expected = cold('--c', { c: VorgangActions.loadVorgangWithEingangFailure({ apiError }) }); + actions = hot('-a', { a: action }); + + expect(effects.loadVorgangWithEingang$).toBeObservable(expected); + }) + }) + + describe('loadVorgangWithEingangSuccess', () => { + + it('should dispatch "loadPendingCommandList" action if link exists', () => { + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource([VorgangWithEingangLinkRel.PENDING_COMMANDS]); + const action = VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang }); + + actions = hot('-a-|', { a: action }); + + const expected = hot('-a-|', { a: VorgangActions.loadPendingCommandList({ vorgangWithEingang }) }); + expect(effects.loadVorgangWithEingangSuccess$).toBeObservable(expected); + }) + + it('should dispatch empty if link not exists', () => { + const action = VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang }); + + actions = hot('a', { a: action }); + + expect(effects.loadVorgangWithEingangSuccess$).toBeObservable(cold('-')); + }) + }) + + describe('loadPendingCommandList', () => { + + const action = VorgangActions.loadPendingCommandList({ vorgangWithEingang }); + const props: LoadCommandListProps = <any>{}; + + it('should dispatch "loadPendingCommandList" action', () => { + effects.createLoadPendingCommandListProps = jest.fn().mockReturnValue(props); + + actions = hot('-a-|', { a: action }); + + const expected = hot('-a-|', { a: loadCommandList(props) }); + expect(effects.loadPendingCommandList$).toBeObservable(expected); + }) + + it('should create props', () => { + effects.createLoadPendingCommandListProps = jest.fn() + actions = of(action); + + effects.loadPendingCommandList$.subscribe(); + + expect(effects.createLoadPendingCommandListProps).toHaveBeenCalledWith(vorgangWithEingang); + }) + + describe('createLoadPendingCommandListProps', () => { + + it('should have uri', () => { + const props: LoadCommandListProps = effects.createLoadPendingCommandListProps(vorgangWithEingang); + + expect(props.resource).toBe(vorgangWithEingang); + }) + + it('should have fileName', () => { + const props: LoadCommandListProps = effects.createLoadPendingCommandListProps(vorgangWithEingang); + + expect(props.linkRel).toBe(VorgangWithEingangLinkRel.PENDING_COMMANDS); + }) + + it('should have "loadPendingCommandsSuccess" as success action', () => { + const props: LoadCommandListProps = effects.createLoadPendingCommandListProps(vorgangWithEingang); + + expect(props.successAction).toEqual(effects.createLoadPendingCommandsSuccessAction); + }) + + it('should have "loadPendingCommandsFailure" as failure action', () => { + const props: LoadCommandListProps = effects.createLoadPendingCommandListProps(vorgangWithEingang); + + expect(props.failureAction).toBe(effects.createLoadPendingCommandsFailureAction); + }) + }) + }) + + describe('loadRepresentationList', () => { + + const action = VorgangActions.loadRepresentationList({ vorgangWithEingang }); + const props: LoadBinaryFileListProps = <any>{}; + + it('should dispatch "loadRepresentationList" action', () => { + effects.createLoadRepresentationListProps = jest.fn().mockReturnValue(props); + + actions = hot('-a-|', { a: action }); + + const expected = hot('-a-|', { a: loadBinaryFileList(props) }); + expect(effects.loadRepresentationList$).toBeObservable(expected); + }) + + it('should create props', () => { + effects.createLoadRepresentationListProps = jest.fn() + actions = of(action); + + effects.loadRepresentationList$.subscribe(); + + expect(effects.createLoadRepresentationListProps).toHaveBeenCalledWith(vorgangWithEingang); + }) + + describe('createLoadRepresentationListProps', () => { + + it('should have resource', () => { + const props: LoadBinaryFileListProps = effects.createLoadRepresentationListProps(vorgangWithEingang); + + expect(props.resource).toBe(vorgangWithEingang); + }) + + it('should have linkRel', () => { + const props: LoadBinaryFileListProps = effects.createLoadRepresentationListProps(vorgangWithEingang); + + expect(props.linkRel).toBe(VorgangWithEingangLinkRel.REPRESENTATIONS); + }) + + it('should have "createLoadRepresentationListSuccess" as success action', () => { + const props: LoadBinaryFileListProps = effects.createLoadRepresentationListProps(vorgangWithEingang); + + expect(props.successAction).toEqual(effects.createLoadRepresentationListSuccessAction); + }) + + it('should have "createLoadRepresentationListFailure" as failure action', () => { + const props: LoadBinaryFileListProps = effects.createLoadRepresentationListProps(vorgangWithEingang); + + expect(props.failureAction).toBe(effects.createLoadRepresentationListFailureAction); + }) + }) + }) + + describe('loadAttachmentList', () => { + + const action = VorgangActions.loadAttachmentList({ vorgangWithEingang }); + const props: LoadBinaryFileListProps = <any>{}; + + it('should dispatch "loadAttachmentList" action', () => { + effects.createLoadAttachmentListProps = jest.fn().mockReturnValue(props); + + actions = hot('-a-|', { a: action }); + + const expected = hot('-a-|', { a: loadBinaryFileList(props) }); + expect(effects.loadAttachmentList$).toBeObservable(expected); + }) + + it('should create props', () => { + effects.createLoadAttachmentListProps = jest.fn() + actions = of(action); + + effects.loadAttachmentList$.subscribe(); + + expect(effects.createLoadAttachmentListProps).toHaveBeenCalledWith(vorgangWithEingang); + }) + + describe('createLoadAttachmentListProps', () => { + + it('should have resource', () => { + const props: LoadBinaryFileListProps = effects.createLoadAttachmentListProps(vorgangWithEingang); + + expect(props.resource).toBe(vorgangWithEingang); + }) + + it('should have linkRel', () => { + const props: LoadBinaryFileListProps = effects.createLoadAttachmentListProps(vorgangWithEingang); + + expect(props.linkRel).toBe(VorgangWithEingangLinkRel.ATTACHMENTS); + }) + + it('should have "createLoadAttachmentListSuccessAction" as success action', () => { + const props: LoadBinaryFileListProps = effects.createLoadAttachmentListProps(vorgangWithEingang); + + expect(props.successAction).toEqual(effects.createLoadAttachmentListSuccessAction); + }) + + it('should have "createLoadAttachmentListFailureAction" as failure action', () => { + const props: LoadBinaryFileListProps = effects.createLoadAttachmentListProps(vorgangWithEingang); + + expect(props.failureAction).toBe(effects.createLoadAttachmentListFailureAction); + }) + }) + }) }); \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.ts index 0eb97d5ffbc567db0dfc5d3ac1677325f14f0ea7..e474a1ca753378cdc756a21a2be4e3b993e2c439 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.effects.ts @@ -24,16 +24,22 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ApiRootFacade } from '@goofy-client/api-root-shared'; -import { getApiErrorFromHttpErrorResponse, isServiceUnavailable } from '@goofy-client/tech-shared'; +import { BinaryFileListResource, LoadBinaryFileListProps, LoadBinaryFileListSuccessProps, loadBinaryFileList } from '@goofy-client/binary-file-shared'; +import { CommandListResource, LoadCommandListProps, LoadCommandListSuccessProps, loadCommandList } from '@goofy-client/command-shared'; +import { ApiError, ApiErrorAction, ResourceUriProps, getApiErrorFromHttpErrorResponse, isServiceUnavailable } from '@goofy-client/tech-shared'; import { SnackBarService } from '@goofy-client/ui'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; +import { TypedAction } from '@ngrx/store/src/models'; +import { hasLink } from '@ngxp/rest'; import { of } from 'rxjs'; -import { catchError, map, switchMap } from 'rxjs/operators'; +import { catchError, filter, map, switchMap } from 'rxjs/operators'; import { VorgangListService } from '../vorgang-list.service'; import { getSearchLinkRel } from '../vorgang-navigation.util'; +import { VorgangWithEingangLinkRel } from '../vorgang.linkrel'; import { VorgangMessages } from '../vorgang.messages'; -import { ApiRootWithLinkRelProps, HttpErrorAction, SearchVorgaengeByProps } from './vorgang.actions'; +import { VorgangWithEingangResource } from '../vorgang.model'; +import { ApiRootWithLinkRelProps, HttpErrorAction, SearchVorgaengeByProps, VorgangWithEingangAction } from './vorgang.actions'; import { VorgangFacade } from './vorgang.facade'; import { VorgangRepository } from './vorgang.repository'; @@ -45,63 +51,127 @@ export class VorgangEffects { constructor(private readonly actions$: Actions, private store: Store, private repository: VorgangRepository, private apiRootFacade: ApiRootFacade, private vorgangFacade: VorgangFacade, private snackbarService: SnackBarService) { } - loadVorgangList$ = createEffect(() => - this.actions$.pipe( - ofType(VorgangActions.loadVorgangList), - switchMap((props: ApiRootWithLinkRelProps) => this.repository.loadVorgangList(props.apiRoot, props.linkRel).pipe( - map(loadedVorgangList => VorgangActions.loadVorgangListSuccess({ vorgangList: loadedVorgangList })), - catchError(error => of(VorgangActions.loadVorgangListFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) - )) - ) + //VorgangList + loadVorgangList$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadVorgangList), + switchMap((props: ApiRootWithLinkRelProps) => this.repository.loadVorgangList(props.apiRoot, props.linkRel).pipe( + map(loadedVorgangList => VorgangActions.loadVorgangListSuccess({ vorgangList: loadedVorgangList })), + catchError(error => of(VorgangActions.loadVorgangListFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) + ))) ) - loadNextPage$ = createEffect(() => - this.actions$.pipe( - ofType(VorgangActions.loadNextPage), - concatLatestFrom(() => this.store.select(VorgangSelectors.vorgangList)), - switchMap(([, vorgangList]) => this.repository.getNextVorgangListPage(vorgangList.resource).pipe( - map(loadedVorgangList => VorgangActions.loadNextPageSuccess({ vorgangList: loadedVorgangList })), - catchError(error => of(VorgangActions.loadVorgangListFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) - )) - ) - ) + loadNextPage$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadNextPage), + concatLatestFrom(() => this.store.select(VorgangSelectors.vorgangList)), + switchMap(([, vorgangList]) => this.repository.getNextVorgangListPage(vorgangList.resource).pipe( + map(loadedVorgangList => VorgangActions.loadNextPageSuccess({ vorgangList: loadedVorgangList })), + catchError(error => of(VorgangActions.loadVorgangListFailure({ apiError: getApiErrorFromHttpErrorResponse(error) }))) + )) + )) - searchVorgaengeBy$ = createEffect(() => - this.actions$.pipe( - ofType(VorgangActions.searchVorgaengeBy), - switchMap((props: SearchVorgaengeByProps) => this.repository.searchVorgaengeBy(props.apiRoot, props.searchString, props.linkRel).pipe( - map(loadedVorgangList => VorgangActions.searchVorgaengeBySuccess({ vorgangList: loadedVorgangList })), - catchError(error => of(VorgangActions.searchVorgaengeByFailure({ httpErrorResponse: error }))) - )) - ) - ) + searchVorgaengeBy$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.searchVorgaengeBy), + switchMap((props: SearchVorgaengeByProps) => this.repository.searchVorgaengeBy(props.apiRoot, props.searchString, props.linkRel).pipe( + map(loadedVorgangList => VorgangActions.searchVorgaengeBySuccess({ vorgangList: loadedVorgangList })), + catchError(error => of(VorgangActions.searchVorgaengeByFailure({ httpErrorResponse: error }))) + )) + )) - searchForPreview$ = createEffect(() => - this.actions$.pipe( - ofType(VorgangActions.searchForPreview), - concatLatestFrom(() => [this.apiRootFacade.getApiRoot(), this.vorgangFacade.getVorgangFilter()]), - switchMap(([stringBasedProps, apiRoot, vorgangFilter]) => { - return this.repository.searchVorgaengeBy(apiRoot.resource, stringBasedProps.string, getSearchLinkRel(vorgangFilter), VorgangListService.SEARCH_PREVIEW_LIST_LIMIT).pipe( - map(loadedVorgangList => VorgangActions.searchForPreviewSuccess({ vorgangList: loadedVorgangList })), - catchError(error => of(VorgangActions.searchForPreviewFailure({ httpErrorResponse: error }))) - ) - }) - ) - ) + searchForPreview$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.searchForPreview), + concatLatestFrom(() => [this.apiRootFacade.getApiRoot(), this.vorgangFacade.getVorgangFilter()]), + switchMap(([stringBasedProps, apiRoot, vorgangFilter]) => this.repository.searchVorgaengeBy(apiRoot.resource, stringBasedProps.string, getSearchLinkRel(vorgangFilter), VorgangListService.SEARCH_PREVIEW_LIST_LIMIT).pipe( + map(loadedVorgangList => VorgangActions.searchForPreviewSuccess({ vorgangList: loadedVorgangList })), + catchError(error => of(VorgangActions.searchForPreviewFailure({ httpErrorResponse: error }))) + )) + )) - showSearchError$ = createEffect(() => - this.actions$.pipe( - ofType( - VorgangActions.searchForPreviewFailure, - VorgangActions.searchVorgaengeByFailure - ), - map((action: HttpErrorAction) => this.showSearchError(action.httpErrorResponse)), - ), { dispatch: false } - ); + showSearchError$ = createEffect(() => this.actions$.pipe( + ofType( + VorgangActions.searchForPreviewFailure, + VorgangActions.searchVorgaengeByFailure + ), + map((action: HttpErrorAction) => this.showSearchError(action.httpErrorResponse)), + ), { dispatch: false }); showSearchError(error: HttpErrorResponse): void { if (isServiceUnavailable(error.error.status)) { this.snackbarService.showError(VorgangMessages.SEARCH_UNAVAILABLE); } } + + //VorgangWithEingang + loadVorgangWithEingang$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadVorgangWithEingang), + switchMap((props: ResourceUriProps) => this.repository.getVorgang(props.resourceUri).pipe( + map(vorgangWithEingang => VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang })), + catchError(error => of(VorgangActions.loadVorgangWithEingangFailure({ apiError: getApiErrorFromHttpErrorResponse(error) })))) + ) + )) + + loadVorgangWithEingangSuccess$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadVorgangWithEingangSuccess), + filter((props: VorgangWithEingangAction) => hasLink(props.vorgangWithEingang, VorgangWithEingangLinkRel.PENDING_COMMANDS)), + switchMap((props: VorgangWithEingangAction) => of(VorgangActions.loadPendingCommandList({ vorgangWithEingang: props.vorgangWithEingang }))) + )) + + loadPendingCommandList$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadPendingCommandList), + switchMap((props: VorgangWithEingangAction) => of(loadCommandList(this.createLoadPendingCommandListProps(props.vorgangWithEingang)))) + )) + + createLoadPendingCommandListProps(vorgangWithEingang: VorgangWithEingangResource): LoadCommandListProps { + return { resource: vorgangWithEingang, linkRel: VorgangWithEingangLinkRel.PENDING_COMMANDS, + successAction: this.createLoadPendingCommandsSuccessAction, + failureAction: this.createLoadPendingCommandsFailureAction + } + } + + createLoadPendingCommandsSuccessAction(commandList: CommandListResource): LoadCommandListSuccessProps & TypedAction<string> { + return VorgangActions.loadPendingCommandListSuccess({ commandList }); + } + + createLoadPendingCommandsFailureAction(apiError: ApiError): ApiErrorAction & TypedAction<string> { + return VorgangActions.loadPendingCommandListFailure({ apiError }); + } + + loadRepresentationList$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadRepresentationList), + switchMap((props: VorgangWithEingangAction) => of(loadBinaryFileList(this.createLoadRepresentationListProps(props.vorgangWithEingang)))) + )) + + createLoadRepresentationListProps(vorgangWithEingang: VorgangWithEingangResource): LoadBinaryFileListProps { + return { resource: vorgangWithEingang, linkRel: VorgangWithEingangLinkRel.REPRESENTATIONS, + successAction: this.createLoadRepresentationListSuccessAction, + failureAction: this.createLoadRepresentationListFailureAction + } + } + + createLoadRepresentationListSuccessAction(binaryFileList: BinaryFileListResource): LoadBinaryFileListSuccessProps & TypedAction<string> { + return VorgangActions.loadRepresentationListSuccess({ binaryFileList }); + } + + createLoadRepresentationListFailureAction(apiError: ApiError): ApiErrorAction & TypedAction<string> { + return VorgangActions.loadRepresentationListFailure({ apiError }); + } + + loadAttachmentList$ = createEffect(() => this.actions$.pipe( + ofType(VorgangActions.loadAttachmentList), + switchMap((props: VorgangWithEingangAction) => of(loadBinaryFileList(this.createLoadAttachmentListProps(props.vorgangWithEingang)))) + )) + + createLoadAttachmentListProps(vorgangWithEingang: VorgangWithEingangResource): LoadBinaryFileListProps { + return { resource: vorgangWithEingang, linkRel: VorgangWithEingangLinkRel.ATTACHMENTS, + successAction: this.createLoadAttachmentListSuccessAction, + failureAction: this.createLoadAttachmentListFailureAction + } + } + + createLoadAttachmentListSuccessAction(binaryFileList: BinaryFileListResource): LoadBinaryFileListSuccessProps & TypedAction<string> { + return VorgangActions.loadAttachmentListSuccess({ binaryFileList }); + } + + createLoadAttachmentListFailureAction(apiError: ApiError): ApiErrorAction & TypedAction<string> { + return VorgangActions.loadAttachmentListFailure({ apiError }); + } } \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.spec.ts index d69426c813824d3368e5549b25aab6ae69866375..1911b2c90dd40f9dc2a881baab3334941ca694bf 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.spec.ts @@ -23,10 +23,13 @@ */ import faker from '@faker-js/faker'; import { ApiRootResource } from '@goofy-client/api-root-shared'; +import { CommandResource } from '@goofy-client/command-shared'; import { createStateResource, StateResource } from '@goofy-client/tech-shared'; import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; import { Store } from '@ngrx/store'; +import { ResourceUri } from '@ngxp/rest'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; +import { createCommandResource } from 'libs/command-shared/test/command'; import { createVorgangListResource, createVorgangResources, createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { Subject } from 'rxjs'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; @@ -58,6 +61,7 @@ describe('VorgangFacade', () => { expect(facade).toBeTruthy(); }) + //VorgangList describe('getVorgangList', () => { it('should return selected value', (done) => { @@ -332,17 +336,7 @@ describe('VorgangFacade', () => { }) }) - describe('setVorgangWithEingang', () => { - - it('should dispatch "loadVorgangWithEingangSuccess" action', () => { - const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); - - facade.setVorgangWithEingang(vorgangWithEingang); - - expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang })); - }) - }) - + //VorgangWithEingang describe('getVorgangWithEingang', () => { it('should return selected value', (done) => { @@ -357,21 +351,141 @@ describe('VorgangFacade', () => { }) }) - describe('reloadVorgangWithEingang', () => { + describe('loadVorgangWithEingang', () => { + + const vorgangWithEingangUri: ResourceUri = faker.internet.url(); + + it('should dispatch "loadVorgangWithEingang" action', () => { + facade.loadVorgangWithEingang(vorgangWithEingangUri); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.loadVorgangWithEingang({ resourceUri: vorgangWithEingangUri })); + }) + }) + + describe('getAttachmentList', () => { - it('should dispatch "setReloadVorgangWithEingang" action', () => { - facade.reloadVorgangWithEingang(); + it('should select from store', () => { + facade.getAttachmentList(); - expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setReloadVorgangWithEingang()); + expect(store.select).toHaveBeenCalledWith(VorgangSelectors.attachmentList); }) }) - describe('loadVorgangWithEingang', () => { + describe('loadAttachmentList', () => { - it('should dispatch "loadVorgangWithEingang" action', () => { - facade.loadVorgangWithEingang(); + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + it('should dispatch "loadAttachmentList" action', () => { + facade.loadAttachmentList(vorgangWithEingang); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.loadAttachmentList({ vorgangWithEingang })); + }) + }) + + describe('getRepresentationList', () => { + + it('should select from store', () => { + facade.getRepresentationList(); + + expect(store.select).toHaveBeenCalledWith(VorgangSelectors.representationList); + }) + }) + + describe('loadRepresentationList', () => { + + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + it('should dispatch "loadRepresentationList" action', () => { + facade.loadRepresentationList(vorgangWithEingang); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.loadRepresentationList({ vorgangWithEingang })); + }) + }) + + describe('clearVorgangWithEingang', () => { + + it('should dispatch "clearnVorgangWithEingang" action', () => { + facade.clearVorgangWithEingang(); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.clearVorgangWithEingang()); + }) + }) + + describe('getForwardPendingCommand', () => { + + it('should select from store', () => { + facade.getForwardPendingCommand(); + + expect(store.select).toHaveBeenCalledWith(VorgangSelectors.forwardPendingCommand); + }) + }) + + describe('setForwardSingleCommandLoading', () => { + + it('should dispatch "setForwardingSingleCommandLoading" action', () => { + facade.setForwardSingleCommandLoading(); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setForwardingSingleCommandLoading()); + }) + }) + + describe('setForwardSingleCommand', () => { + + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + it('should dispatch "setForwardSingleCommand" action', () => { + facade.setForwardSingleCommand(commandStateResource); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setForwardingSingleCommand({ commandStateResource })); + }) + }) + + describe('getSendPostfachNachrichtPendingCommand', () => { + + it('should select from store', () => { + facade.getSendPostfachNachrichtPendingCommand(); + + expect(store.select).toHaveBeenCalledWith(VorgangSelectors.sendPostfachNachrichtPendingCommand); + }) + }) + + describe('setPendingSendPostfachMailSingleCommandLoading', () => { + + it('should dispatch "setSendPostfachNachrichtSingleCommandLoading" action', () => { + facade.setPendingSendPostfachMailSingleCommandLoading(); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setSendPostfachNachrichtSingleCommandLoading()); + }) + }) + + describe('setPendingSendPostfachMailSingleCommand', () => { + + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + it('should dispatch "setSendPostfachNachrichtSingleCommand" action', () => { + facade.setPendingSendPostfachMailSingleCommand(commandStateResource); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setSendPostfachNachrichtSingleCommand({ commandStateResource })); + }) + }) + + describe('assignUser', () => { + + it('should dispatch "assignUser" action', () => { + facade.assignUser(); + + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.assignUser()); + }) + }) + + describe('assignUserSuccess', () => { + + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + it('should dispatch "assignUser" action', () => { + facade.assignUserSuccess(commandStateResource); - expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.loadVorgangWithEingang()); + expect(store.dispatch).toHaveBeenCalledWith(VorgangActions.setAssignUser({ commandStateResource })); }) }) }) \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.ts index ff1d6a43a92bfbb253c4380e53af5f62b8702fde..8c101394cb6c4f0f3275120ee98c3ec053f0005c 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.facade.ts @@ -23,8 +23,11 @@ */ import { Injectable } from '@angular/core'; import { ApiRootResource } from '@goofy-client/api-root-shared'; +import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; +import { CommandResource } from '@goofy-client/command-shared'; import { StateResource } from '@goofy-client/tech-shared'; import { Store } from '@ngrx/store'; +import { ResourceUri } from '@ngxp/rest'; import { Observable } from 'rxjs'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; import { backButtonUrl, isVorgangFilterSelected, isVorgangViewSelected } from './vorgang.selectors'; @@ -112,19 +115,61 @@ export class VorgangFacade { } //VorgangWithEingang - public setVorgangWithEingang(vorgangWithEingang: VorgangWithEingangResource): void{ - this.store.dispatch(VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang })); - } - public getVorgangWithEingang(): Observable<StateResource<VorgangWithEingangResource>> { return this.store.select(VorgangSelectors.vorgangWithEingang); } - public reloadVorgangWithEingang(): void { - this.store.dispatch(VorgangActions.setReloadVorgangWithEingang()); + public loadVorgangWithEingang(vorgangWithEingangUri: ResourceUri): void { + this.store.dispatch(VorgangActions.loadVorgangWithEingang({ resourceUri: vorgangWithEingangUri })); + } + + public getAttachmentList(): Observable<StateResource<BinaryFileListResource>> { + return this.store.select(VorgangSelectors.attachmentList); + } + + public loadAttachmentList(vorgangWithEingang: VorgangWithEingangResource): void{ + this.store.dispatch(VorgangActions.loadAttachmentList({ vorgangWithEingang })); + } + + public getRepresentationList(): Observable<StateResource<BinaryFileListResource>> { + return this.store.select(VorgangSelectors.representationList); + } + + public loadRepresentationList(vorgangWithEingang: VorgangWithEingangResource): void { + this.store.dispatch(VorgangActions.loadRepresentationList({ vorgangWithEingang })); + } + + public clearVorgangWithEingang(): void { + this.store.dispatch(VorgangActions.clearVorgangWithEingang()); } - public loadVorgangWithEingang(): void { - this.store.dispatch(VorgangActions.loadVorgangWithEingang()); + + public getForwardPendingCommand(): Observable<StateResource<CommandResource>> { + return this.store.select(VorgangSelectors.forwardPendingCommand); + } + public setForwardSingleCommandLoading(): void { + this.store.dispatch(VorgangActions.setForwardingSingleCommandLoading()) + } + public setForwardSingleCommand(commandStateResource: StateResource<CommandResource>): void { + this.store.dispatch(VorgangActions.setForwardingSingleCommand({ commandStateResource })); + } + + + public getSendPostfachNachrichtPendingCommand(): Observable<StateResource<CommandResource>> { + return this.store.select(VorgangSelectors.sendPostfachNachrichtPendingCommand); + } + public setPendingSendPostfachMailSingleCommandLoading(): void { + this.store.dispatch(VorgangActions.setSendPostfachNachrichtSingleCommandLoading()) + } + public setPendingSendPostfachMailSingleCommand(commandStateResource: StateResource<CommandResource>): void { + this.store.dispatch(VorgangActions.setSendPostfachNachrichtSingleCommand({ commandStateResource })); + } + + + public assignUser(): void { + this.store.dispatch(VorgangActions.assignUser()); + } + public assignUserSuccess(commandStateResource: StateResource<CommandResource>): void { + this.store.dispatch(VorgangActions.setAssignUser({ commandStateResource })); } } \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts index 0938902ad79c911f47b2e3ae4fd58b485bc61e3b..8f4c7c3bff41da8cc8d5d5d8faf01eaf965b27d3 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.spec.ts @@ -23,21 +23,26 @@ */ import { HttpErrorResponse } from '@angular/common/http'; import { UrlSegment } from '@angular/router'; +import faker from '@faker-js/faker'; import { ApiRootResource } from '@goofy-client/api-root-shared'; +import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; +import { CommandListResource, CommandOrder, CommandResource } from '@goofy-client/command-shared'; import { RouteData } from '@goofy-client/navigation-shared'; -import { ApiError, createEmptyStateResource, createStateResource, EMPTY_ARRAY, EMPTY_STRING } from '@goofy-client/tech-shared'; +import { ApiError, EMPTY_ARRAY, EMPTY_STRING, StateResource, createEmptyStateResource, createStateResource } from '@goofy-client/tech-shared'; import { Action } from '@ngrx/store'; +import { ResourceUri } from '@ngxp/rest'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; +import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; +import { createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; import { createRouteData } from 'libs/navigation-shared/test/navigation-test-factory'; import { createVorgangListResource, createVorgangListResourceWithResource, createVorgangResource, createVorgangResources, createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { createApiError, createHttpErrorResponse } from '../../../../tech-shared/test/error'; -import { MEINE_ROUTE_PARAM, ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW } from '../vorgang-navigation.util'; +import { MEINE_ROUTE_PARAM, ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW, VORGANG_WITH_EINGANG_ROUTE_PARAM } from '../vorgang-navigation.util'; import { VorgangListLinkRel } from '../vorgang.linkrel'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; -import { initialState, reducer, VorgangState } from './vorgang.reducer'; +import { VorgangState, initialState, reducer } from './vorgang.reducer'; import * as Storage from '../../../../app-shared/src/storage/storage'; -import * as CommandActions from '../../../../command-shared/src/lib/+state/command.actions'; import * as NavigationActions from '../../../../navigation-shared/src/lib/+state/navigation.actions'; import * as VorgangActions from './vorgang.actions'; import * as Reducer from './vorgang.reducer'; @@ -212,7 +217,6 @@ describe('Vorgang Reducer', () => { expect(state.vorgangList.error).toStrictEqual(apiError); }) }) - }) describe('searchForPreview', () => { @@ -282,6 +286,248 @@ describe('Vorgang Reducer', () => { }) }) + describe('updateLocalStorage', () => { + + it('should update filter', () => { + const spy = jest.spyOn(Storage, 'setFilterIntoStorage'); + + Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.NEU); + + expect(spy).toHaveBeenCalledWith('meine'); + }) + + describe('update view', () => { + + it('should clear view if view is vorgangList', () => { + const spy = jest.spyOn(Storage, 'removeLocalStorageView'); + + Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.VORGANG_LIST); + + expect(spy).toHaveBeenCalled(); + }) + + it('should set to given view', () => { + const spy = jest.spyOn(Storage, 'setViewIntoStorage'); + + Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.ANGENOMMEN); + + expect(spy).toHaveBeenCalledWith('angenommen'); + }) + }) + }) + + describe('on "clearSearchString" action', () => { + + it('should empty searchString', () => { + const action = VorgangActions.clearSearchString(); + const emptySearchString: string = EMPTY_STRING; + + const state: VorgangState = reducer(initialState, action); + + expect(state.searchString).toStrictEqual(emptySearchString); + }) + }) + + //VorgangWithEingang + describe('vorgangWithEingang', () => { + + describe('on "loadVorgangWithEingang" action', () => { + + const resourceUri: ResourceUri = faker.internet.url(); + + it('should set loading to true', () => { + const action = VorgangActions.loadVorgangWithEingang({ resourceUri }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.vorgangWithEingang.loading).toBeTruthy(); + }) + }) + + describe('on "loadVorgangWithEingangSuccess" action', () => { + + it('should set vorgangWithEingang', () => { + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + const action = VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.vorgangWithEingang.resource).toStrictEqual(vorgangWithEingang); + }) + }) + + describe('on "setReloadVorgangWithEingang" action', () => { + + it('should set vorgangWithEingang#reload to true', () => { + const action = VorgangActions.setReloadVorgangWithEingang(); + + const state: VorgangState = reducer(initialState, action); + + expect(state.vorgangWithEingang.reload).toBeTruthy(); + }) + }) + }) + + describe('attachmentList', () => { + + describe('on "loadAttachmentList" action', () => { + + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + it('should set state resource loading', () => { + const action = VorgangActions.loadAttachmentList({ vorgangWithEingang }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.attachmentList.loading).toBeTruthy(); + }) + }) + + describe('on "loadAttachmentListSuccess" action', () => { + + it('should set state resource', () => { + const binaryFileList: BinaryFileListResource = createBinaryFileListResource(); + const action = VorgangActions.loadAttachmentListSuccess({ binaryFileList }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.attachmentList.resource).toBe(binaryFileList); + }) + }) + }) + + describe('representationList', () => { + + describe('on "loadRepresentationList" action', () => { + + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + + it('should set state resource loading', () => { + const action = VorgangActions.loadRepresentationList({ vorgangWithEingang }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.representationList.loading).toBeTruthy(); + }) + }) + + describe('on "loadRepresentationListSuccess" action', () => { + + it('should set state resource', () => { + const binaryFileList: BinaryFileListResource = createBinaryFileListResource(); + const action = VorgangActions.loadRepresentationListSuccess({ binaryFileList }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.representationList.resource).toBe(binaryFileList); + }) + }) + }) + + describe('pending commands', () => { + + describe('on "loadPendingCommandListSuccess" action', () => { + + it('should set forward command', () => { + const forwardCommand: CommandResource = { ...createCommandResource(), order: CommandOrder.REDIRECT_VORGANG }; + const commandList: CommandListResource = createCommandListResource([createCommandResource(), forwardCommand]); + const action = VorgangActions.loadPendingCommandListSuccess({ commandList }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.forwardPendingCommand.resource).toBe(forwardCommand); + }) + + it('should set send postfach nachricht command', () => { + const sendPostfachNachrichtCommand: CommandResource = { ...createCommandResource(), order: CommandOrder.SEND_POSTFACH_NACHRICHT }; + const commandList: CommandListResource = createCommandListResource([createCommandResource(), sendPostfachNachrichtCommand]); + const action = VorgangActions.loadPendingCommandListSuccess({ commandList }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.sendPostfachNachrichtPendingCommand.resource).toBe(sendPostfachNachrichtCommand); + }) + }) + }) + + describe('forwardPendingCommand', () => { + + describe('on "setForwardingSingleCommandLoading" action', () => { + + it('should set forward pending command loading', () => { + const action = VorgangActions.setForwardingSingleCommandLoading(); + + const state: VorgangState = reducer(initialState, action); + + expect(state.forwardPendingCommand.loading).toBeTruthy(); + }) + }) + + describe('on "setForwardingSingleCommand" action', () => { + + it('should set forward pending command', () => { + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + const action = VorgangActions.setForwardingSingleCommand({ commandStateResource }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.forwardPendingCommand).toBe(commandStateResource); + }) + }) + }) + + describe('sendPostfachNachrichtPendingCommand', () => { + + describe('on "setSendPostfachNachrichtSingleCommandLoading" action', () => { + + it('should set send postfach nachricht pending command loading', () => { + const action = VorgangActions.setSendPostfachNachrichtSingleCommandLoading(); + + const state: VorgangState = reducer(initialState, action); + + expect(state.sendPostfachNachrichtPendingCommand.loading).toBeTruthy(); + }) + }) + + describe('on "setSendPostfachNachrichtSingleCommand" action', () => { + + it('should set forward pending command', () => { + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + const action = VorgangActions.setSendPostfachNachrichtSingleCommand({ commandStateResource }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.sendPostfachNachrichtPendingCommand).toBe(commandStateResource); + }) + }) + }) + + describe('assignUser', () => { + + describe('on "assignUser" action', () => { + + it('should set assign user state resource loading', () => { + const action = VorgangActions.assignUser(); + + const state: VorgangState = reducer(initialState, action); + + expect(state.assignUserCommand.loading).toBeTruthy(); + }) + }) + + describe('on "setAssignUser" action', () => { + + it('should set assignUser command', () => { + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + const action = VorgangActions.setAssignUser({ commandStateResource }); + + const state: VorgangState = reducer(initialState, action); + + expect(state.assignUserCommand).toBe(commandStateResource); + }) + }) + }) + describe('updateByRouteData', () => { describe('contains search route', () => { @@ -316,7 +562,6 @@ describe('Vorgang Reducer', () => { it('should be updated by given vorgangFilter and vorgangView', () => { const urlSegments: UrlSegment[] = [<any>{ path: ROUTE_PARAM_BY_VORGANG_FILTER[VorgangFilter.ALLE] }, { path: ROUTE_PARAM_BY_VORGANG_VIEW[VorgangView.ANGENOMMEN] }]; const routeData: RouteData = { ...createRouteData(), urlSegments }; - //TODO Typisieren const localStorageSpy = jest.spyOn(Reducer, 'updateLocalStorage'); Reducer.updateByRouteData(initialState, routeData); @@ -327,7 +572,6 @@ describe('Vorgang Reducer', () => { it('should be updated by given vorgangFilter and default view', () => { const urlSegments: UrlSegment[] = [<any>{ path: ROUTE_PARAM_BY_VORGANG_FILTER[VorgangFilter.ALLE] }]; const routeData: RouteData = { ...createRouteData(), urlSegments }; - //TODO Typisieren const localStorageSpy = jest.spyOn(Reducer, 'updateLocalStorage'); Reducer.updateByRouteData(initialState, routeData); @@ -398,97 +642,40 @@ describe('Vorgang Reducer', () => { }) }) }) - }) - - describe('updateLocalStorage', () => { - - it('should update filter', () => { - const spy = jest.spyOn(Storage, 'setFilterIntoStorage'); - - Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.NEU); - - expect(spy).toHaveBeenCalledWith('meine'); - }) - - describe('update view', () => { - - it('should clear view if view is vorgangList', () => { - const spy = jest.spyOn(Storage, 'removeLocalStorageView'); - - Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.VORGANG_LIST); - expect(spy).toHaveBeenCalled(); - }) + describe('contains vorgang route', () => { - it('should set to given view', () => { - const spy = jest.spyOn(Storage, 'setViewIntoStorage'); + const queryParameter = { [VORGANG_WITH_EINGANG_ROUTE_PARAM]: faker.internet.url() }; + const routeData: RouteData = { ...createRouteData(), queryParameter }; - Reducer.updateLocalStorage(VorgangFilter.MEINE_VORGAENGE, VorgangView.ANGENOMMEN); + it('should set reload to vorgangWithEingang', () => { + const state: VorgangState = Reducer.updateByRouteData(initialState, routeData); - expect(spy).toHaveBeenCalledWith('angenommen'); + expect(state.vorgangWithEingang.reload).toBeTruthy(); }) - }) - }) - - describe('on "clearSearchString" action', () => { - - it('should empty searchString', () => { - const action = VorgangActions.clearSearchString(); - const emptySearchString: string = EMPTY_STRING; - - const state: VorgangState = reducer(initialState, action); - - expect(state.searchString).toStrictEqual(emptySearchString); - }) - }) - - describe('vorgangWithEingang', () => { - describe('on "loadVorgangWithEingang" action', () => { - - it('should set loading to true', () => { - const action = VorgangActions.loadVorgangWithEingang(); - - const state: VorgangState = reducer(initialState, action); + it('should clear attachmentList', () => { + const state: VorgangState = Reducer.updateByRouteData(initialState, routeData); - expect(state.vorgangWithEingang.loading).toBeTruthy(); + expect(state.attachmentList).toEqual(createEmptyStateResource()); }) - }) - - describe('on "loadVorgangWithEingangSuccess" action', () => { - - it('should set vorgangWithEingang', () => { - const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); - const action = VorgangActions.loadVorgangWithEingangSuccess({ vorgangWithEingang }); - const state: VorgangState = reducer(initialState, action); + it('should clear representationList', () => { + const state: VorgangState = Reducer.updateByRouteData(initialState, routeData); - expect(state.vorgangWithEingang.resource).toStrictEqual(vorgangWithEingang); + expect(state.representationList).toEqual(createEmptyStateResource()); }) - }) - describe('on "setReloadVorgangWithEingang" action', () => { - - it('should set vorgangWithEingang#reload to true', () => { - const action = VorgangActions.setReloadVorgangWithEingang(); + it('should clear forwardPendingCommand', () => { + const state: VorgangState = Reducer.updateByRouteData(initialState, routeData); - const state: VorgangState = reducer(initialState, action); - - expect(state.vorgangWithEingang.reload).toBeTruthy(); + expect(state.forwardPendingCommand).toEqual(createEmptyStateResource()); }) - }) - }) - - describe('command', () => { - describe('on "publishConcurrentModificationAction" action', () => { - - it('should set vorgangWithEingang#reload to true', () => { - const action = CommandActions.publishConcurrentModificationAction(); + it('should clear sendPostfachNachrichtPendingCommand', () => { + const state: VorgangState = Reducer.updateByRouteData(initialState, routeData); - const state: VorgangState = reducer(initialState, action); - - expect(state.vorgangWithEingang.reload).toBeTruthy(); + expect(state.sendPostfachNachrichtPendingCommand).toEqual(createEmptyStateResource()); }) }) }) diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts index f9653714e444d06e1316059fd3f13f9e30387a87..96a83a6c24459b406a17fa0f681848fb8f4c5db5 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.reducer.ts @@ -22,16 +22,17 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { removeLocalStorageView, setFilterIntoStorage, setViewIntoStorage } from '@goofy-client/app-shared'; +import { BinaryFileListResource, LoadBinaryFileListSuccessProps } from '@goofy-client/binary-file-shared'; +import { CommandOrder, CommandResource, CommandStateResourceProps, LoadCommandListSuccessProps, getPendingCommandByOrder } from '@goofy-client/command-shared'; import { RouteData } from '@goofy-client/navigation-shared'; -import { createEmptyStateResource, createErrorStateResource, createStateResource, EMPTY_ARRAY, EMPTY_STRING, getApiErrorFromHttpErrorResponse, StateResource } from '@goofy-client/tech-shared'; +import { ApiErrorAction, EMPTY_ARRAY, EMPTY_STRING, StateResource, createEmptyStateResource, createErrorStateResource, createStateResource, getApiErrorFromHttpErrorResponse } from '@goofy-client/tech-shared'; import { Action, ActionReducer, createReducer, on } from '@ngrx/store'; import { isNil } from 'lodash-es'; -import { getSearchString, getVorgangFilter, getVorgangView, isUebersichtsSeite, ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW } from '../vorgang-navigation.util'; +import { ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW, getSearchString, getVorgangFilter, getVorgangView, isUebersichtsSeite, isVorgangPage } from '../vorgang-navigation.util'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; import { getVorgaengeFromList } from '../vorgang.util'; -import { ApiErrorAction, HttpErrorAction, StringBasedProps, VorgangListAction, VorgangWithEingangAction } from './vorgang.actions'; +import { HttpErrorAction, StringBasedProps, VorgangListAction, VorgangWithEingangAction } from './vorgang.actions'; -import * as CommandActions from '../../../../command-shared/src/lib/+state/command.actions'; import * as NavigationActions from '../../../../navigation-shared/src/lib/+state/navigation.actions'; import * as VorgangActions from './vorgang.actions'; import * as NavigationReducer from './vorgang.reducer'; @@ -51,6 +52,11 @@ export interface VorgangState { vorgangFilter: VorgangFilter; vorgangWithEingang: StateResource<VorgangWithEingangResource>; + attachmentList: StateResource<BinaryFileListResource>; + representationList: StateResource<BinaryFileListResource>; + assignUserCommand: StateResource<CommandResource>; + forwardPendingCommand: StateResource<CommandResource>; + sendPostfachNachrichtPendingCommand: StateResource<CommandResource>; } export const initialState: VorgangState = { @@ -61,11 +67,17 @@ export const initialState: VorgangState = { vorgangView: VorgangView.VORGANG_LIST, vorgangFilter: VorgangFilter.ALLE, - vorgangWithEingang: createEmptyStateResource() + vorgangWithEingang: createEmptyStateResource(), + attachmentList: createEmptyStateResource(), + representationList: createEmptyStateResource(), + assignUserCommand: createEmptyStateResource(), + forwardPendingCommand: createEmptyStateResource(), + sendPostfachNachrichtPendingCommand: createEmptyStateResource() }; const vorgangReducer: ActionReducer<VorgangState, Action> = createReducer( initialState, + //VorgangList on(VorgangActions.loadVorgangList, (state: VorgangState): VorgangState => ({ ...state, @@ -95,6 +107,7 @@ const vorgangReducer: ActionReducer<VorgangState, Action> = createReducer( })), + //Search on(VorgangActions.searchVorgaengeBy, (state: VorgangState): VorgangState => ({ ...state, vorgangList: { ...state.vorgangList, loading: true }, @@ -156,7 +169,11 @@ const vorgangReducer: ActionReducer<VorgangState, Action> = createReducer( })), on(VorgangActions.loadVorgangWithEingangSuccess, (state: VorgangState, props: VorgangWithEingangAction): VorgangState => ({ ...state, - vorgangWithEingang: createStateResource(props.vorgangWithEingang) + vorgangWithEingang: createStateResource(props.vorgangWithEingang), + })), + on(VorgangActions.loadVorgangWithEingangFailure, (state: VorgangState, props: ApiErrorAction): VorgangState => ({ + ...state, + vorgangWithEingang: createErrorStateResource(props.apiError) })), on(VorgangActions.setReloadVorgangWithEingang, (state: VorgangState): VorgangState => ({ ...state, @@ -164,42 +181,115 @@ const vorgangReducer: ActionReducer<VorgangState, Action> = createReducer( })), + on(VorgangActions.loadAttachmentList, (state: VorgangState): VorgangState => ({ + ...state, + attachmentList: { ...state.attachmentList, loading: true } + })), + on(VorgangActions.loadAttachmentListSuccess, (state: VorgangState, props: LoadBinaryFileListSuccessProps): VorgangState => ({ + ...state, + attachmentList: createStateResource(props.binaryFileList) + })), + + on(VorgangActions.loadRepresentationList, (state: VorgangState): VorgangState => ({ + ...state, + representationList: { ...state.representationList, loading: true } + })), + on(VorgangActions.loadRepresentationListSuccess, (state: VorgangState, props: LoadBinaryFileListSuccessProps): VorgangState => ({ + ...state, + representationList: createStateResource(props.binaryFileList) + })), + + + on(VorgangActions.loadPendingCommandListSuccess, (state: VorgangState, props: LoadCommandListSuccessProps): VorgangState => ({ + ...state, + forwardPendingCommand: createStateResource(getPendingCommandByOrder(props.commandList, [CommandOrder.REDIRECT_VORGANG])), + sendPostfachNachrichtPendingCommand: createStateResource(getPendingCommandByOrder(props.commandList, [CommandOrder.SEND_POSTFACH_NACHRICHT])) + })), + + on(VorgangActions.setForwardingSingleCommandLoading, (state: VorgangState): VorgangState => ({ + ...state, + forwardPendingCommand: createEmptyStateResource(true), + })), + on(VorgangActions.setForwardingSingleCommand, (state: VorgangState, props: CommandStateResourceProps): VorgangState => ({ + ...state, + forwardPendingCommand: props.commandStateResource, + })), + + on(VorgangActions.setSendPostfachNachrichtSingleCommandLoading, (state: VorgangState): VorgangState => ({ + ...state, + sendPostfachNachrichtPendingCommand: createEmptyStateResource(true), + })), + on(VorgangActions.setSendPostfachNachrichtSingleCommand, (state: VorgangState, props: CommandStateResourceProps): VorgangState => ({ + ...state, + sendPostfachNachrichtPendingCommand: props.commandStateResource, + })), + + + on(VorgangActions.assignUser, (state: VorgangState): VorgangState => ({ + ...state, + assignUserCommand: {...state.assignUserCommand, loading: true }, + })), + on(VorgangActions.setAssignUser, (state: VorgangState, props: CommandStateResourceProps): VorgangState => ({ + ...state, + assignUserCommand: props.commandStateResource, + })), + + //Navigation on(NavigationActions.updateCurrentRouteData, (state, action): VorgangState => { return NavigationReducer.updateByRouteData(state, action.routeData); }), - - - //Command - on(CommandActions.publishConcurrentModificationAction, (state: VorgangState): VorgangState => ({ - ...state, - vorgangWithEingang: { ...state.vorgangWithEingang, reload: true } - })) ); function clearPreviewList(props: StringBasedProps): boolean { return isNil(props.string) || props.string === EMPTY_STRING || props.string.length <= 3; } -//TODO remove export as soon as mock private functions is possible export function updateByRouteData(state: VorgangState, routeData: RouteData): VorgangState { let newState = { ...state, vorgangList: {...state.vorgangList, reload: true }, vorgaenge: EMPTY_ARRAY }; - if (isUebersichtsSeite(routeData)){ - newState = { ...newState, vorgangFilter: getVorgangFilter(routeData), vorgangView: getVorgangView(routeData) }; + if (isUebersichtsSeite(routeData)) { + newState = prepareStateOnVorgangListNavigation(newState, routeData); + } + if (isVorgangPage(routeData)) { + newState = prepareStateOnVorgangNavigation(newState); + } + return newState; +} - NavigationReducer.updateLocalStorage(newState.vorgangFilter, newState.vorgangView); +function prepareStateOnVorgangListNavigation(state: VorgangState, routeData: RouteData): VorgangState { + let newState: VorgangState = { ...state, + vorgangFilter: getVorgangFilter(routeData), + vorgangView: getVorgangView(routeData), + vorgangWithEingang: createEmptyStateResource(), + attachmentList: createEmptyStateResource(), + representationList: createEmptyStateResource(), + forwardPendingCommand: createEmptyStateResource(), + sendPostfachNachrichtPendingCommand: createEmptyStateResource() + }; - const searchString: string = getSearchString(routeData); - newState = { ...newState, searchString }; + NavigationReducer.updateLocalStorage(newState.vorgangFilter, newState.vorgangView); - if(state.searchString != getSearchString(routeData)) { - newState = { ...newState, searchPreviewList: { ...state.searchPreviewList, reload: true }}; - } + const searchString: string = getSearchString(routeData); + newState = { ...newState, searchString }; + + if (state.searchString != getSearchString(routeData)) { + newState = { ...newState, searchPreviewList: { ...state.searchPreviewList, reload: true }}; } + return newState; } +function prepareStateOnVorgangNavigation(state: VorgangState): VorgangState { + return {...state, + vorgangWithEingang: { ...state.vorgangWithEingang, reload: true }, + attachmentList: createEmptyStateResource(), + representationList: createEmptyStateResource(), + forwardPendingCommand: createEmptyStateResource(), + sendPostfachNachrichtPendingCommand: createEmptyStateResource() + } +} + export function updateLocalStorage(vorgangFilter: VorgangFilter, vorgangView: VorgangView): void { setFilterIntoStorage(ROUTE_PARAM_BY_VORGANG_FILTER[vorgangFilter]); diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts index bc9d21a39bc60b6ac0ca61212fb48aa237db6f87..f78105f9ad9a1b164ad91211ca7f8530c1ea2a32 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.spec.ts @@ -21,10 +21,14 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; +import { CommandResource } from '@goofy-client/command-shared'; +import { StateResource, createStateResource } from '@goofy-client/tech-shared'; +import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; +import { createCommandResource } from 'libs/command-shared/test/command'; import { createVorgangListResource, createVorgangResources, createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; -import { initialState, VorgangPartialState } from './vorgang.reducer'; +import { VorgangPartialState, initialState } from './vorgang.reducer'; import * as VorgangNavigationUtil from '../vorgang-navigation.util'; import * as VorgangSelectors from './vorgang.selectors'; @@ -41,6 +45,10 @@ describe('Vorgang Selectors', () => { const vorgangView: VorgangView = VorgangView.VORGANG_LIST; const vorgangWithEingang: StateResource<VorgangWithEingangResource> = createStateResource(createVorgangWithEingangResource()); + const attachmentList: StateResource<BinaryFileListResource> = createStateResource(createBinaryFileListResource()); + const representationList: StateResource<BinaryFileListResource> = createStateResource(createBinaryFileListResource()); + const forwardPendingCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); + const sendPostfachNachrichtPendingCommand: StateResource<CommandResource> = createStateResource(createCommandResource()); beforeEach(() => { state = { @@ -52,7 +60,12 @@ describe('Vorgang Selectors', () => { searchString, vorgangFilter, vorgangView, - vorgangWithEingang + + vorgangWithEingang, + attachmentList, + representationList, + forwardPendingCommand, + sendPostfachNachrichtPendingCommand } }; }); @@ -104,13 +117,8 @@ describe('Vorgang Selectors', () => { }) }) - it('should return vorgangWithEingang', () => { - const result: StateResource<VorgangWithEingangResource> = VorgangSelectors.vorgangWithEingang.projector(state.VorgangState); - - expect(result).toBe(vorgangWithEingang); - }) - describe('isVorgangViewSelected', () => { + it('should return true if state and view matches', () => { const result: boolean = VorgangSelectors.isVorgangViewSelected(VorgangView.VORGANG_LIST).projector(vorgangView); @@ -125,10 +133,54 @@ describe('Vorgang Selectors', () => { }) describe('getVorgangViewRoutePath', () => { + it('should return /alle/neu', () => { const result: string = VorgangSelectors.getVorgangViewRoutePath(VorgangView.NEU).projector(vorgangFilter); expect(result).toBe('/alle/neu'); }) }) + + //VorgangWithEingang + it('should return vorgangWithEingang', () => { + const result: StateResource<VorgangWithEingangResource> = VorgangSelectors.vorgangWithEingang.projector(state.VorgangState); + + expect(result).toBe(vorgangWithEingang); + }) + + describe('attachmentList', () => { + + it('should Return attachmentList from state', () => { + const result: StateResource<BinaryFileListResource> = VorgangSelectors.attachmentList.projector(state.VorgangState); + + expect(result).toBe(attachmentList); + }) + }) + + describe('representationList', () => { + + it('should Return representationList from state', () => { + const result: StateResource<BinaryFileListResource> = VorgangSelectors.representationList.projector(state.VorgangState); + + expect(result).toBe(representationList); + }) + }) + + describe('forwardPendingCommand', () => { + + it('should return command from state', () => { + const result: StateResource<CommandResource> = VorgangSelectors.forwardPendingCommand.projector(state.VorgangState); + + expect(result).toBe(forwardPendingCommand); + }) + }) + + describe('send postfach command', () => { + + it('should return command from state', () => { + const result: StateResource<CommandResource> = VorgangSelectors.sendPostfachNachrichtPendingCommand.projector(state.VorgangState); + + expect(result).toBe(sendPostfachNachrichtPendingCommand); + }) + }) }); \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts index ec9b52aeb4ea89532e22df5185615bc9007cbb2a..f00b5cbb203b2ceae9d5c0f9dba4111b59726f1f 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/+state/vorgang.selectors.ts @@ -21,11 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; +import { CommandResource } from '@goofy-client/command-shared'; import { EMPTY_STRING, StateResource } from '@goofy-client/tech-shared'; -import { createFeatureSelector, createSelector, MemoizedSelector } from '@ngrx/store'; +import { MemoizedSelector, createFeatureSelector, createSelector } from '@ngrx/store'; import { buildBackButtonUrl, buildVorgangFilterViewRoutePath } from '../vorgang-navigation.util'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView, VorgangWithEingangResource } from '../vorgang.model'; -import { VorgangState, VORGANG_FEATURE_KEY } from './vorgang.reducer'; +import { VORGANG_FEATURE_KEY, VorgangState } from './vorgang.reducer'; export const getVorgangState: MemoizedSelector<object, VorgangState> = createFeatureSelector<VorgangState>(VORGANG_FEATURE_KEY); @@ -46,4 +48,9 @@ export const getVorgangViewRoutePath = (view: VorgangView) => createSelector(vor export const isVorgangFilterSelected = (filter: VorgangFilter) => createSelector(vorgangFilter, (vorgangFilterInState: VorgangFilter) => vorgangFilterInState === filter); export const getVorgangFilterRoutePath = (filter: VorgangFilter) => createSelector(vorgangView, searchString, (vorgangViewInState: VorgangView, searchStringInState: string) => buildVorgangFilterViewRoutePath(filter, vorgangViewInState, searchStringInState)); +//VorgangWithEingang export const vorgangWithEingang: MemoizedSelector<VorgangState, StateResource<VorgangWithEingangResource>> = createSelector(getVorgangState, (state: VorgangState) => state.vorgangWithEingang); +export const attachmentList: MemoizedSelector<VorgangState, StateResource<BinaryFileListResource>> = createSelector(getVorgangState, (state: VorgangState) => state.attachmentList); +export const representationList: MemoizedSelector<VorgangState, StateResource<BinaryFileListResource>> = createSelector(getVorgangState, (state: VorgangState) => state.representationList); +export const forwardPendingCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector(getVorgangState, (state: VorgangState) => state.forwardPendingCommand); +export const sendPostfachNachrichtPendingCommand: MemoizedSelector<VorgangState, StateResource<CommandResource>> = createSelector(getVorgangState, (state: VorgangState) => state.sendPostfachNachrichtPendingCommand); \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.spec.ts index ce1e882566a5693a009e5c5f22d589f5b338d9df..7b39fd6b4dfacb6250eb81420b935e5515f4749b 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.spec.ts @@ -24,12 +24,12 @@ import { faker } from '@faker-js/faker'; import { CommandResource, CommandService, CreateCommand } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; -import { createEmptyStateResource, createStateResource, StateResource } from '@goofy-client/tech-shared'; +import { StateResource, createEmptyStateResource, createStateResource } from '@goofy-client/tech-shared'; import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; import { SnackBarService } from '@goofy-client/ui'; import { hot } from 'jest-marbles'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; -import { CommandErrorMessage, COMMAND_ERROR_MESSAGES } from 'libs/command-shared/src/lib/command.message'; +import { COMMAND_ERROR_MESSAGES, CommandErrorMessage } from 'libs/command-shared/src/lib/command.message'; import { createCommandErrorResource, createCommandResource } from 'libs/command-shared/test/command'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { EMPTY, of } from 'rxjs'; @@ -59,7 +59,7 @@ describe('VorgangCommandService', () => { navigationService.urlChanged = jest.fn(); navigationService.urlChanged.mockReturnValue(EMPTY); - service = new VorgangCommandService(useFromMock(navigationService), useFromMock(commandService), useFromMock(snackBarService), useFromMock(vorgangService), useFromMock(vorgangFacade)); + service = new VorgangCommandService(useFromMock(navigationService), useFromMock(commandService), useFromMock(snackBarService), useFromMock(vorgangService)); }) describe('onNavigation', () => { @@ -173,7 +173,7 @@ describe('VorgangCommandService', () => { beforeEach(() => { commandService.createCommand.mockReturnValue(of(commandStateResource)); - vorgangService.getVorgang.mockReturnValue(vorgangResource); + vorgangService.getVorgangWithEingang.mockReturnValue(of(vorgangResource)); }) it('should set VorgangOrder loading', () => { @@ -193,7 +193,7 @@ describe('VorgangCommandService', () => { it('should call vorgangService to get Vorgang', () => { service.doVorgangOrderAction(linkRel, commandProvider, snackbarMessage, vorgangOrder); - expect(vorgangService.getVorgang).toHaveBeenCalled(); + expect(vorgangService.getVorgangWithEingang).toHaveBeenCalled(); }) describe('handleCreatedVorgangOrderCommand', () => { diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.ts index c95e02dc4ce946106e89338f5870bc1173850b97..40997637eb04bcc9b31e55d22602445e21ac4127 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang-command.service.ts @@ -25,11 +25,10 @@ import { Injectable } from '@angular/core'; import { Params } from '@angular/router'; import { CommandResource, CommandService, CreateCommand, hasError, isConcurrentModification, isDone } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; -import { createEmptyStateResource, isNotUndefined, StateResource } from '@goofy-client/tech-shared'; +import { StateResource, createEmptyStateResource, isNotNull, isNotUndefined } from '@goofy-client/tech-shared'; import { SnackBarService } from '@goofy-client/ui'; import { COMMAND_ERROR_MESSAGES } from 'libs/command-shared/src/lib/command.message'; -import { BehaviorSubject, filter, Observable, Subscription } from 'rxjs'; -import { VorgangFacade } from './+state/vorgang.facade'; +import { BehaviorSubject, Observable, Subscription, filter } from 'rxjs'; import { VorgangWithEingangLinkRel } from './vorgang.linkrel'; import { VorgangMessages } from './vorgang.messages'; import { VorgangOrder } from './vorgang.model'; @@ -60,8 +59,7 @@ export class VorgangCommandService { private navigationService: NavigationService, private commandService: CommandService, private snackBarService: SnackBarService, - private vorgangService: VorgangService, - private vorgangFacade: VorgangFacade + private vorgangService: VorgangService ) { this.listenForLeavingVorgang(); } @@ -131,9 +129,15 @@ export class VorgangCommandService { doVorgangOrderAction(linkRel: string, commandProvider: () => CreateCommand, snackBarMessage: string, order: VorgangOrder): void { this.setVorgangOrderOnLoading(order); - this.vorgangActionSubscription = this.commandService.createCommand(this.vorgangService.getVorgang(), linkRel, commandProvider()) - .pipe(filter(commandStateResource => isDone(commandStateResource.resource))) - .subscribe((commandStateResource: StateResource<CommandResource>) => this.handleCreatedVorgangOrderCommand(commandStateResource, snackBarMessage, order)) + const vorgangSubscription = this.vorgangService.getVorgangWithEingang().subscribe(vorgangWithEingang => { + if(isNotNull(vorgangWithEingang.resource)){ + setTimeout(() => vorgangSubscription.unsubscribe(), 0); + + this.vorgangActionSubscription = this.commandService.createCommand(vorgangWithEingang.resource, linkRel, commandProvider()) + .pipe(filter(commandStateResource => isDone(commandStateResource.resource))) + .subscribe((commandStateResource: StateResource<CommandResource>) => this.handleCreatedVorgangOrderCommand(commandStateResource, snackBarMessage, order)) + } + }) }; handleCreatedVorgangOrderCommand(commandStateResource: StateResource<CommandResource>, snackBarMessage: string, order: VorgangOrder): void { diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang-list.service.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang-list.service.ts index c05cc19a0487686bfd43c2819957fad113a6897b..b1b4119f8a0579fe8b80e2389ae7982ed0c53897 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang-list.service.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang-list.service.ts @@ -23,11 +23,11 @@ */ import { Injectable } from '@angular/core'; import { ApiRootFacade, ApiRootResource } from '@goofy-client/api-root-shared'; -import { createEmptyStateResource, doIfLoadingRequired, EMPTY_STRING, isNotNull, StateResource } from '@goofy-client/tech-shared'; -import { combineLatest, Observable } from 'rxjs'; +import { EMPTY_STRING, StateResource, createEmptyStateResource, doIfLoadingRequired, isNotNull } from '@goofy-client/tech-shared'; +import { Observable, combineLatest } from 'rxjs'; import { map, startWith, tap, withLatestFrom } from 'rxjs/operators'; import { VorgangFacade } from './+state/vorgang.facade'; -import { buildLinkRel, getSearchLinkRel, ROUTE_PARAM_BY_VORGANG_FILTER } from './vorgang-navigation.util'; +import { ROUTE_PARAM_BY_VORGANG_FILTER, buildLinkRel, getSearchLinkRel } from './vorgang-navigation.util'; import { VorgangFilter, VorgangListResource, VorgangResource, VorgangView } from './vorgang.model'; @Injectable({ providedIn: 'root' }) @@ -67,9 +67,11 @@ export class VorgangListService { return this.vorgangFacade.getVorgangFilter().pipe(map((vorgangFilter: VorgangFilter) => ROUTE_PARAM_BY_VORGANG_FILTER[vorgangFilter])); } + //Wird nicht genutzt/gebraucht, kann raus public getVorgangFilterRoutePath(vorgangFilter: VorgangFilter): Observable<string> { return this.vorgangFacade.getVorgangFilterRoutePath(vorgangFilter); } + // public isVorgangViewSelected(vorgangView: VorgangView): Observable<boolean> { return this.vorgangFacade.isVorgangViewSelected(vorgangView); diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.spec.ts index e39cd90cc86fd42fb70e1ade7dd7a8febb1d723a..f1e5f961d228b92dbb215a2fc9a226c7fde46b7b 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.spec.ts @@ -27,9 +27,10 @@ import { RouteData } from '@goofy-client/navigation-shared'; import { EMPTY_STRING } from '@goofy-client/tech-shared'; import { createRouteData } from 'libs/navigation-shared/test/navigation-test-factory'; import { initialState, VorgangState } from './+state/vorgang.reducer'; -import { ALLE_ROUTE_PARAM, buildLinkRel, buildPathSegmentsFromLocalStorage, buildVorgangFilterViewRoutePath, buildVorgangListRouteWithVorgangFilter, getSearchLinkRel, getSearchString, getVorgangFilter, ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW, SEARCH_ROUTE_PARAM } from './vorgang-navigation.util'; +import { ALLE_ROUTE_PARAM, buildLinkRel, buildPathSegmentsFromLocalStorage, buildVorgangFilterViewRoutePath, buildVorgangListRouteWithVorgangFilter, getSearchLinkRel, getSearchString, getVorgangFilter, isVorgangPage, ROUTE_PARAM_BY_VORGANG_FILTER, ROUTE_PARAM_BY_VORGANG_VIEW, SEARCH_ROUTE_PARAM } from './vorgang-navigation.util'; import { VorgangFilter, VorgangView } from './vorgang.model'; +import faker from '@faker-js/faker'; import * as Storage from '../../../../libs/app-shared/src/storage/storage'; import * as VorgangNavigationUtil from './vorgang-navigation.util'; @@ -397,4 +398,17 @@ describe('Vorgang Navigation Util', () => { const urlSegments: UrlSegment[] = [<any>{ path: ROUTE_PARAM_BY_VORGANG_FILTER[vorgangFilter] }]; return { ...createRouteData(), urlSegments }; } + + describe('isVorgangPage', () => { + + it('should return true on matching param and segment length', () => { + const is: boolean = isVorgangPage(buildVorgangPageRouteData()); + + expect(is).toBeTruthy(); + }) + + function buildVorgangPageRouteData(): RouteData { + return { ...createRouteData(), urlSegments: [], queryParameter: { [VorgangNavigationUtil.VORGANG_WITH_EINGANG_ROUTE_PARAM]: faker.internet.url() } }; + } + }) }) \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.ts index 7c542f2991555cb7b3bdb5043dc89ccaf656ecf8..1591075d5a99090458419504fb9213f75495d269 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang-navigation.util.ts @@ -24,7 +24,7 @@ import { ApiRootLinkRel } from '@goofy-client/api-root-shared'; import { getFilterFromLocalStorage, getViewFromLocalStorage } from '@goofy-client/app-shared'; import { RouteData } from '@goofy-client/navigation-shared'; -import { EMPTY_STRING, isNotEmpty, isNotUndefined } from '@goofy-client/tech-shared'; +import { EMPTY_STRING, isNotEmpty, isNotNull, isNotUndefined } from '@goofy-client/tech-shared'; import { isEmpty } from 'lodash-es'; import { VorgangState } from './+state/vorgang.reducer'; import { VorgangFilter, VorgangView } from './vorgang.model'; @@ -122,7 +122,6 @@ export function buildBackButtonUrl(state: VorgangState): string { return '/' + VorgangNavigationUtil.buildPathSegmentsFromLocalStorage().join('/'); } -//TODO remove export as soon as mock private functions is possible export function isStateEqualLocalStorage(state: VorgangState): boolean { return getFilterFromLocalStorage() === ROUTE_PARAM_BY_VORGANG_FILTER[state.vorgangFilter] && getViewFromLocalStorage() === ROUTE_PARAM_BY_VORGANG_VIEW[state.vorgangView]; } @@ -131,7 +130,6 @@ export function buildVorgangListRoutePathWithFilter(state: VorgangState, filter: return VorgangNavigationUtil.buildVorgangListRouteWithVorgangFilter(state, filter); } -//TODO export entfernen sobald mocken von private functions moeglich ist export function buildVorgangListRouteWithVorgangFilter(state: VorgangState, filter: VorgangFilter): string { let route = '/' + ROUTE_PARAM_BY_VORGANG_FILTER[filter]; @@ -175,3 +173,15 @@ export function buildPathSegmentsFromLocalStorage(): string[] { return pathSegments; } + +export function isVorgangPage(routeData: RouteData): boolean { + return hasSegements(routeData, 0) && hasVorgangParam(routeData); +} + +function hasVorgangParam(routeData: RouteData): boolean { + return isNotNull(routeData.queryParameter[VORGANG_WITH_EINGANG_ROUTE_PARAM]); +} + +function hasSegements(routeData: RouteData, numberOfSegements: number): boolean { + return routeData.urlSegments.length === numberOfSegements; +} diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts index a10986c6b3b06f36e3035604d107f09806cecbc7..c1864009b7670b925e7c013c3174553e8a8f0d7a 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.spec.ts @@ -23,7 +23,8 @@ */ import { faker } from '@faker-js/faker'; import { ApiRootResource, ApiRootService } from '@goofy-client/api-root-shared'; -import { CommandListResource, CommandResource, CommandService } from '@goofy-client/command-shared'; +import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; +import { CommandResource, CommandService } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; import { StateResource, createEmptyStateResource, createStateResource } from '@goofy-client/tech-shared'; import { Mock, mock, useFromMock } from '@goofy-client/test-utils'; @@ -32,11 +33,10 @@ import { cold, hot } from 'jest-marbles'; import { createApiRootResource } from 'libs/api-root-shared/test/api-root'; import { createBinaryFileListResource } from 'libs/binary-file-shared/test/binary-file'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; -import { createCommandListResource, createCommandResource } from 'libs/command-shared/test/command'; +import { createCommandResource } from 'libs/command-shared/test/command'; import { createVorgangWithEingangResource } from 'libs/vorgang-shared/test/vorgang'; import { of } from 'rxjs'; import { VorgangFacade } from './+state/vorgang.facade'; -import { VorgangRepository } from './+state/vorgang.repository'; import { VorgangWithEingangLinkRel } from './vorgang.linkrel'; import { CreateAssignUserCommand, VorgangOrder, VorgangWithEingangResource } from './vorgang.model'; import { VorgangService } from './vorgang.service'; @@ -44,42 +44,37 @@ import { VorgangService } from './vorgang.service'; describe('VorgangService', () => { let service: VorgangService; let navigationService: Mock<NavigationService>; - let repository: Mock<VorgangRepository>; let commandService: Mock<CommandService>; - let vorgangFacade: Mock<VorgangFacade>; + let facade: Mock<VorgangFacade>; let apiRootService: Mock<ApiRootService>; beforeEach(() => { - repository = mock(VorgangRepository); navigationService = { ...mock(NavigationService) }; commandService = mock(CommandService); - vorgangFacade = mock(VorgangFacade); + facade = mock(VorgangFacade); apiRootService = mock(ApiRootService); navigationService.urlChanged = jest.fn(); navigationService.urlChanged.mockReturnValue(of({})); - service = new VorgangService(useFromMock(repository), useFromMock(navigationService), useFromMock(commandService), useFromMock(vorgangFacade), useFromMock(apiRootService)); + service = new VorgangService(useFromMock(navigationService), useFromMock(commandService), useFromMock(facade), useFromMock(apiRootService)); }) + const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + const vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource> = createStateResource(vorgangWithEingang); + describe('getVorgangWithEingang', () => { - const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); - const vorgangWithEingangStateResource: StateResource<VorgangWithEingangResource> = createStateResource(vorgangWithEingang); const apiRootStateResource: StateResource<ApiRootResource> = createStateResource(createApiRootResource()); beforeEach(() => { - repository.getVorgang.mockReturnValue(of(vorgangWithEingang)); - service.setStateResourceOnLoading = jest.fn(); apiRootService.getApiRoot.mockReturnValue(of(apiRootStateResource)); }) it('should get vorgangWithEingang', () => { - service.getVorgangAsObservable = jest.fn(); - service.getVorgangWithEingang(); - expect(service.getVorgangAsObservable).toBeCalled(); + expect(facade.getVorgangWithEingang).toBeCalled(); }) it('should get getApiRoot', () => { @@ -92,8 +87,7 @@ describe('VorgangService', () => { beforeEach(() => { apiRootService.getApiRoot.mockReturnValue(hot('-a', { a: apiRootStateResource })); - - service.vorgangWithEingang$.next(vorgangWithEingangStateResource); + facade.getVorgangWithEingang.mockReturnValue(hot('-a', { a: vorgangWithEingangStateResource })); }) it('should return value', () => { @@ -105,283 +99,241 @@ describe('VorgangService', () => { describe('loadVorgangWithEingang', () => { - beforeEach(() => { - service.loadVorgangWithEingang = jest.fn(); - }) - it('should be called if loading required', () => { - service.vorgangWithEingang$.next(createEmptyStateResource()); + facade.getVorgangWithEingang.mockReturnValue(of(createEmptyStateResource())); service.getVorgangWithEingang().subscribe(); - expect(service.loadVorgangWithEingang).toHaveBeenCalled(); + expect(facade.loadVorgangWithEingang).toHaveBeenCalled(); }) it('should NOT be called if already loaded', () => { - service.vorgangWithEingang$.next(vorgangWithEingangStateResource); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); service.getVorgangWithEingang().subscribe(); - expect(service.loadVorgangWithEingang).not.toHaveBeenCalled(); + expect(facade.loadVorgangWithEingang).not.toHaveBeenCalled(); }) }) }) - describe('loadVorgangWithEingang', () => { - const vorgangWithEingang = createVorgangWithEingangResource(); - const url = getUrl(vorgangWithEingang); + describe('getVorgangWithEingangUri', () => { - beforeEach(() => { - repository.getVorgang.mockReturnValue(cold('a', { a: vorgangWithEingang })); + it('should call facade', () => { + service.getVorgangWithEingangUri(); + + expect(navigationService.getDecodedParam).toHaveBeenCalledWith(VorgangService.VORGANG_WITH_EINGANG_URL); }) + }) - it('should set resource loading', () => { - service.setStateResourceOnLoading = jest.fn(); + describe('getAttachments', () => { - service.loadVorgangWithEingang(url); + const binaryFile: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileStateResource: StateResource<BinaryFileListResource> = createStateResource(binaryFile); - expect(service.setStateResourceOnLoading).toHaveBeenCalled(); + beforeEach(() => { + facade.getAttachmentList.mockReturnValue(of(binaryFileStateResource)); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); }) - it('should call repository', () => { - service.loadVorgangWithEingang(url); + it('should get AttachmentList by facade', () => { + service.getAttachments(); - expect(repository.getVorgang).toHaveBeenCalledWith(url); + expect(facade.getAttachmentList).toHaveBeenCalled(); }) - it('should update vorgang', () => { - service.updateVorgang = jest.fn(); - const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); - repository.getVorgang.mockReturnValue(of(vorgangWithEingang)); + it('should get vorgangWithEingang by facade', () => { + service.getAttachments(); - service.loadVorgangWithEingang(url); - - expect(service.updateVorgang).toHaveBeenCalledWith(vorgangWithEingang); + expect(facade.getVorgangWithEingang).toHaveBeenCalled(); }) - }) - describe('updateVorgang', () => { - - beforeEach(() => { - service.loadPendingCommandsByVorgang = jest.fn(); - service.loadAttachments = jest.fn(); - }) + describe('initial', () => { - it('should setVorgangWithEingang', () => { - service.setVorgangWithEingang = jest.fn(); - const vorgangWithEingang: VorgangWithEingangResource = createVorgangWithEingangResource(); + beforeEach(() => { + facade.getAttachmentList.mockReturnValue(hot('-a', { a: binaryFileStateResource })); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); + }) - service.updateVorgang(vorgangWithEingang); + it('should return value', () => { + const vorgangList = service.getAttachments(); - expect(service.setVorgangWithEingang).toHaveBeenCalledWith(createStateResource(vorgangWithEingang)); + expect(vorgangList).toBeObservable(cold('ab', { a: createEmptyStateResource(true), b: binaryFileStateResource })); + }) }) - it('should load pendingCommands', () => { - service.updateVorgang(createVorgangWithEingangResource()); + describe('loadAttachments', () => { - expect(service.loadPendingCommandsByVorgang).toHaveBeenCalled(); - }) - }) + it('should be called if loading required', () => { + facade.getAttachmentList.mockReturnValue(of(createEmptyStateResource())); - describe('loadPendingCommandsByVorgang', () => { - const vorgangWithEingang = createVorgangWithEingangResource([VorgangWithEingangLinkRel.PENDING_COMMANDS]); + service.getAttachments().subscribe(); - beforeEach(() => { - commandService.getPendingCommands = jest.fn(); - commandService.getPendingCommands.mockReturnValue(of(createCommandResource())); + expect(facade.loadAttachmentList).toHaveBeenCalledWith(vorgangWithEingang) + }) - service.setStateResourceOnLoading = jest.fn(); - service.clearPendingForwardCommand = jest.fn(); - }) + it('should NOT be called if already loaded', () => { + facade.getAttachmentList.mockReturnValue(of(binaryFileStateResource)); - it('should do nothing without link', () => { - service.loadPendingCommandsByVorgang(createVorgangWithEingangResource()); + service.getAttachments().subscribe(); - expect(service.setStateResourceOnLoading).not.toHaveBeenCalled(); - }) + expect(facade.loadAttachmentList).not.toHaveBeenCalled(); + }) - it('should call commandService', () => { - service.loadPendingCommandsByVorgang(vorgangWithEingang); + it('should NOT be called if vorgangWithEingang is not loaded', () => { + facade.getVorgangWithEingang.mockReturnValue(of(createEmptyStateResource())); - expect(commandService.getPendingCommands).toHaveBeenCalledWith(vorgangWithEingang, VorgangWithEingangLinkRel.PENDING_COMMANDS); - }); + service.getAttachments().subscribe(); - describe('handlePendingCommands', () => { + expect(facade.loadAttachmentList).not.toHaveBeenCalled(); + }) + }) + }) - const commandListResource: CommandListResource = createCommandListResource(); + describe('getRepresentations', () => { - it('should call handlePendingSendPostfachMailCommand', () => { - service.handlePendingSendPostfachMailCommand = jest.fn(); + const binaryFile: BinaryFileListResource = createBinaryFileListResource(); + const binaryFileStateResource: StateResource<BinaryFileListResource> = createStateResource(binaryFile); - service.handlePendingCommands(commandListResource); + beforeEach(() => { + facade.getRepresentationList.mockReturnValue(of(binaryFileStateResource)); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); + }) - expect(service.handlePendingSendPostfachMailCommand).toHaveBeenCalledWith(commandListResource); - }); + it('should get RepresentationList by facade', () => { + service.getRepresentations(); - it('should call setPendingForwardCommand', () => { - service.setPendingForwardCommand = jest.fn(); + expect(facade.getRepresentationList).toHaveBeenCalled(); + }) - service.handlePendingCommands(commandListResource); + it('should get vorgangWithEingang by facade', () => { + service.getRepresentations(); - expect(service.setPendingForwardCommand).toHaveBeenCalledWith(commandListResource); - }); + expect(facade.getVorgangWithEingang).toHaveBeenCalled(); }) - describe('setPendingForwardCommand', () => { + describe('initial', () => { beforeEach(() => { - service.setPendingForwardSingleCommand = jest.fn(); + facade.getRepresentationList.mockReturnValue(hot('-a', { a: binaryFileStateResource })); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); }) - it('should set pendingForwardCommand if its empty', () => { - service.pendingForwardCommand$.next(createEmptyStateResource()); - const command: CommandResource = { ...createCommandResource(), order: VorgangOrder.FORWARD }; - const commandList: CommandListResource = createCommandListResource([command]); - - service.setPendingForwardCommand(commandList); - - expect(service.setPendingForwardSingleCommand).toHaveBeenCalledWith(createStateResource(command)); - }) - - it('should NOT set pendingForwardCommand if its exists', () => { - service.pendingForwardCommand$.next(createStateResource(<any>{})); - - service.setPendingForwardCommand(createCommandListResource()); + it('should return value', () => { + const vorgangList = service.getRepresentations(); - expect(service.setPendingForwardSingleCommand).not.toHaveBeenCalled(); + expect(vorgangList).toBeObservable(cold('ab', { a: createEmptyStateResource(true), b: binaryFileStateResource })); }) }) - }) - describe('onNavigation', () => { + describe('loadRepresentationList', () => { + + it('should be called if loading required', () => { + facade.getRepresentationList.mockReturnValue(of(createEmptyStateResource())); - describe('to vorgang list page', () => { + service.getRepresentations().subscribe(); - beforeEach(() => { - service.clearVorgang = jest.fn(); - service.clearPendingForwardCommand = jest.fn(); - service.clearPendingSendPostfachMailCommand = jest.fn(); - service.clearAttachments = jest.fn(); + expect(facade.loadRepresentationList).toHaveBeenCalledWith(vorgangWithEingang) }) - it('should clear vorgang', () => { - service.onNavigation({}); - - expect(service.clearVorgang).toHaveBeenCalled(); - }); + it('should NOT be called if already loaded', () => { + facade.getRepresentationList.mockReturnValue(of(binaryFileStateResource)); - it('should call clearPendingForwardCommand', () => { - service.onNavigation({}); + service.getRepresentations().subscribe(); - expect(service.clearPendingForwardCommand).toHaveBeenCalled(); + expect(facade.loadRepresentationList).not.toHaveBeenCalled(); }) - it('should call clearPendingSendPostfachMailCommand', () => { - service.onNavigation({}); + it('should NOT be called if vorgangWithEingang is not loaded', () => { + facade.getVorgangWithEingang.mockReturnValue(of(createEmptyStateResource())); - expect(service.clearPendingSendPostfachMailCommand).toHaveBeenCalled(); - }) - - it('should call clearAttachments', () => { - service.onNavigation({}); + service.getRepresentations().subscribe(); - expect(service.clearAttachments).toHaveBeenCalled(); + expect(facade.loadRepresentationList).not.toHaveBeenCalled(); }) }) + }) - describe('to vorgang detail page', () => { - - it('should reload current vorgang', () => { - service.reloadCurrentVorgang = jest.fn(); + describe('clearVorgang', () => { - service.onNavigation({ 'vorgangWithEingangUrl': 'X' }); + it('should call facade', () => { + service.clearVorgang(); - expect(service.reloadCurrentVorgang).toHaveBeenCalled(); - }) + expect(facade.clearVorgangWithEingang).toHaveBeenCalled(); }) }) - describe('loadAttachments', () => { + describe('setPendingForwardSingleCommandLoading', () => { - beforeEach(() => { - service.setStateResourceOnLoading = jest.fn(); - repository.getAttachments.mockReturnValue(of(createBinaryFileListResource())); + it('should call facade', () => { + service.setPendingForwardSingleCommandLoading(); + + expect(facade.setForwardSingleCommandLoading).toHaveBeenCalled(); }) + }) - it('should set loading flag', () => { - service.loadAttachments(); + describe('getForwardPendingCommand', () => { - expect(service.setStateResourceOnLoading).toHaveBeenCalled(); + beforeEach(() => { + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); + facade.getForwardPendingCommand.mockReturnValue(of(createEmptyStateResource())); }) - it('should call repository', () => { - service.loadAttachments(); + it('should call facade', () => { + service.getPendingForwardCommand(); - expect(repository.getAttachments).toHaveBeenCalledWith(service.vorgangWithEingang$.value.resource); + expect(facade.getForwardPendingCommand).toHaveBeenCalled(); }) }) - describe('loadRepresentations', () => { + describe('setPendingForwardSingleCommand', () => { - beforeEach(() => { - service.setStateResourceOnLoading = jest.fn(); - repository.getRepresentations.mockReturnValue(of(createBinaryFileListResource())); - }) + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); - it('should set loading flag', () => { - service.loadRepresentations(); + it('should call facade', () => { + service.setPendingForwardSingleCommand(commandStateResource); - expect(service.setStateResourceOnLoading).toHaveBeenCalled(); + expect(facade.setForwardSingleCommand).toHaveBeenCalledWith(commandStateResource); }) + }) + + describe('setPendingForwardSingleCommandLoading', () => { - it('should call repository', () => { - service.loadRepresentations(); + it('should call facade', () => { + service.setPendingForwardSingleCommandLoading(); - expect(repository.getRepresentations).toHaveBeenCalledWith(service.vorgangWithEingang$.value.resource); + expect(facade.setForwardSingleCommandLoading).toHaveBeenCalled(); }) }) - describe('reload vorgang', () => { - - const commandResourceWithLink: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + describe('getPendingSendPostfachMailCommand', () => { beforeEach(() => { - service.loadVorgangWithEingang = jest.fn(); + facade.getVorgangWithEingang.mockReturnValue(of(vorgangWithEingangStateResource)); + facade.getSendPostfachNachrichtPendingCommand.mockReturnValue(of(createEmptyStateResource())); }) - it('should reload with given uri', () => { - service.reloadVorgang(commandResourceWithLink); + it('should call facade', () => { + service.getPendingSendPostfachMailCommand(); - expect(service.loadVorgangWithEingang).toHaveBeenCalledWith(getUrl(commandResourceWithLink, CommandLinkRel.EFFECTED_RESOURCE)); + expect(facade.getSendPostfachNachrichtPendingCommand).toHaveBeenCalled(); }) }) - describe('reloadVorgangOnDone', () => { - - const commandResourceWithResourceLink: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); - const commandResourceWithUpdateLink: CommandResource = createCommandResource([CommandLinkRel.UPDATE]); + describe('setPendingForwardSingleCommand', () => { - beforeEach(() => { - service.loadVorgangWithEingang = jest.fn(); - }) + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); - it('should call load vorgang on command is done', () => { - service.reloadVorgangOnDone(createStateResource(commandResourceWithResourceLink)); - - expect(service.loadVorgangWithEingang).toHaveBeenCalled(); - }) - - it('should not load vorgang on command is pending', () => { - service.reloadVorgangOnDone(createStateResource(commandResourceWithUpdateLink)); + it('should call facade', () => { + service.setPendingForwardSingleCommand(commandStateResource); - expect(service.loadVorgangWithEingang).not.toHaveBeenCalled(); + expect(facade.setForwardSingleCommand).toHaveBeenCalledWith(commandStateResource); }) }) describe('reloadCurrentVorgang', () => { beforeEach(() => { - service.loadVorgangWithEingang = jest.fn(); navigationService.getDecodedParam.mockReturnValue('decodedParameterUrlForTest'); }) @@ -394,7 +346,7 @@ describe('VorgangService', () => { it('should call loadVorgangWithEingang', () => { service.reloadCurrentVorgang(); - expect(service.loadVorgangWithEingang).toHaveBeenCalled(); + expect(facade.loadVorgangWithEingang).toHaveBeenCalled(); }) }) @@ -407,14 +359,20 @@ describe('VorgangService', () => { beforeEach(() => { commandService.createCommand.mockReturnValue(of(commandStateResource)); - service.setAssignUserCommandLoading = jest.fn(); service.setAssignUserCommand = jest.fn(); + facade.getVorgangWithEingang.mockReturnValue(of(createStateResource(vorgangWithEingang))); }) - it('should set assign user command loading', () => { + it('should call facade assignUser', () => { service.assignUser(userProfileUri); - expect(service.setAssignUserCommandLoading).toHaveBeenCalled(); + expect(facade.assignUser).toHaveBeenCalled(); + }) + + it('should call facade to get vorgangWithEingang', () => { + service.assignUser(userProfileUri); + + expect(facade.getVorgangWithEingang).toHaveBeenCalled(); }) it('should call command service', () => { @@ -422,7 +380,7 @@ describe('VorgangService', () => { service.assignUser(userProfileUri).subscribe(); - expect(commandService.createCommand).toHaveBeenCalledWith(service.vorgangWithEingang$.value.resource, VorgangWithEingangLinkRel.ASSIGN, order); + expect(commandService.createCommand).toHaveBeenCalledWith(vorgangWithEingang, VorgangWithEingangLinkRel.ASSIGN, order); }) it('should reload vorgang on command is done', () => { @@ -433,20 +391,61 @@ describe('VorgangService', () => { expect(service.reloadVorgang).toHaveBeenCalled(); }) - it.skip('FIXME: should set assign user command on command is done', () => { + it('should set assign user command on command is done', () => { commandService.createCommand.mockReturnValue(of(commandStateResource)); - service.assignUser(userProfileUri); + + service.assignUser(userProfileUri).subscribe(); expect(service.setAssignUserCommand).toHaveBeenCalledWith(commandStateResource); }) }) + describe('reloadVorgangOnDone', () => { + + const commandResourceWithResourceLink: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + const commandResourceWithUpdateLink: CommandResource = createCommandResource([CommandLinkRel.UPDATE]); + + it('should call load vorgang on command is done', () => { + service.reloadVorgangOnDone(createStateResource(commandResourceWithResourceLink)); + + expect(facade.loadVorgangWithEingang).toHaveBeenCalled(); + }) + + it('should not load vorgang on command is pending', () => { + service.reloadVorgangOnDone(createStateResource(commandResourceWithUpdateLink)); + + expect(facade.loadVorgangWithEingang).not.toHaveBeenCalled(); + }) + }) + + describe('setAssignUserCommand', () => { + + const commandStateResource: StateResource<CommandResource> = createStateResource(createCommandResource()); + + it('should call facade', () => { + service.setAssignUserCommand(commandStateResource); + + expect(facade.assignUserSuccess).toHaveBeenCalledWith(commandStateResource); + }) + }) + + describe('reloadVorgang', () => { + + const command: CommandResource = createCommandResource([CommandLinkRel.EFFECTED_RESOURCE]); + + it('should call facade', () => { + service.reloadVorgang(command); + + expect(facade.loadVorgangWithEingang).toHaveBeenCalledWith(getUrl(command, CommandLinkRel.EFFECTED_RESOURCE)); + }) + }) + describe('getBackButtonUrl', () => { it('should call facade', () => { service.getBackButtonUrl(); - expect(vorgangFacade.getBackButtonUrl).toHaveBeenCalled(); + expect(facade.getBackButtonUrl).toHaveBeenCalled(); }) }) }) diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.ts index 3f066a27224c01ce9eda6ebc70256e56a8ccb8bb..58b28c5739ac75ad9f8385cb49344a6fa324b17f 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang.service.ts @@ -22,227 +22,102 @@ * unter der Lizenz sind dem Lizenztext zu entnehmen. */ import { Injectable } from '@angular/core'; -import { Params } from '@angular/router'; import { ApiRootService } from '@goofy-client/api-root-shared'; import { BinaryFileListResource } from '@goofy-client/binary-file-shared'; -import { CommandListResource, CommandResource, CommandService, getPendingCommandByOrder, hasError, isDone } from '@goofy-client/command-shared'; +import { CommandResource, CommandService, hasError, isDone } from '@goofy-client/command-shared'; import { NavigationService } from '@goofy-client/navigation-shared'; -import { PostfachOrder } from '@goofy-client/postfach-shared'; -import { StateResource, createEmptyStateResource, createStateResource, doIfLoadingRequired, isNotNil, isNotNull, isNotUndefined } from '@goofy-client/tech-shared'; -import { ResourceUri, getUrl, hasLink } from '@ngxp/rest'; +import { StateResource, createEmptyStateResource, doIfLoadingRequired, isNotNil, isNotNull } from '@goofy-client/tech-shared'; +import { ResourceUri, getUrl } from '@ngxp/rest'; import { CommandLinkRel } from 'libs/command-shared/src/lib/command.linkrel'; -import { BehaviorSubject, Observable, Subscription, combineLatest } from 'rxjs'; -import { first, map, startWith, tap } from 'rxjs/operators'; +import { Observable, Subscription, combineLatest } from 'rxjs'; +import { map, mergeMap, startWith, tap, withLatestFrom } from 'rxjs/operators'; import { VorgangFacade } from './+state/vorgang.facade'; -import { VorgangRepository } from './+state/vorgang.repository'; import { VorgangWithEingangLinkRel } from './vorgang.linkrel'; -import { CreateAssignUserCommand, VorgangOrder, VorgangWithEingangResource } from './vorgang.model'; +import { VorgangWithEingangResource } from './vorgang.model'; +import { createAssignUserCommand } from './vorgang.util'; @Injectable({ providedIn: 'root' }) export class VorgangService { - readonly pendingForwardCommand$: BehaviorSubject<StateResource<CommandResource>> = new BehaviorSubject<StateResource<CommandResource>>(createEmptyStateResource<CommandResource>()); - private readonly pendingSendPostfachMailCommand$: BehaviorSubject<StateResource<CommandResource>> = new BehaviorSubject<StateResource<CommandResource>>(createEmptyStateResource<CommandResource>()); - - private readonly attachments$: BehaviorSubject<StateResource<BinaryFileListResource>> = new BehaviorSubject<StateResource<BinaryFileListResource>>(createEmptyStateResource<BinaryFileListResource>()); - private readonly representations$: BehaviorSubject<StateResource<BinaryFileListResource>> = new BehaviorSubject<StateResource<BinaryFileListResource>>(createEmptyStateResource<BinaryFileListResource>()); - - private readonly assignUserCommand$: BehaviorSubject<StateResource<CommandResource>> = new BehaviorSubject<StateResource<CommandResource>>(createEmptyStateResource<CommandResource>()); - - readonly vorgangWithEingang$: BehaviorSubject<StateResource<VorgangWithEingangResource>> = new BehaviorSubject<StateResource<VorgangWithEingangResource>>(createEmptyStateResource<VorgangWithEingangResource>()); - - private navigationSubscription: Subscription; private assignUserSubscription: Subscription; public static readonly VORGANG_WITH_EINGANG_URL: string = 'vorgangWithEingangUrl'; constructor( - private vorgangRepository: VorgangRepository, private navigationService: NavigationService, private commandService: CommandService, - private vorgangFacade: VorgangFacade, + private facade: VorgangFacade, private apiRootService: ApiRootService - ) { - this.listenForLeavingVorgang(); - } + ) { } public getVorgangWithEingang(): Observable<StateResource<VorgangWithEingangResource>> { - return combineLatest([this.getVorgangAsObservable(), this.apiRootService.getApiRoot()]).pipe( + return combineLatest([this.facade.getVorgangWithEingang(), this.apiRootService.getApiRoot()]).pipe( tap(([vorgangWithEingang, apiRoot]) => { - if(isNotNull(apiRoot.resource)) doIfLoadingRequired(vorgangWithEingang, () => this.loadVorgangWithEingang(this.getVorgangWithEingangUrl())); + if(isNotNull(apiRoot.resource)) doIfLoadingRequired(vorgangWithEingang, () => this.facade.loadVorgangWithEingang(this.getVorgangWithEingangUri())); }), map(([vorgangWithEingang,]) => vorgangWithEingang), startWith(createEmptyStateResource<VorgangWithEingangResource>(true))); } - getVorgangAsObservable(): Observable<StateResource<VorgangWithEingangResource>>{ - return this.vorgangWithEingang$.asObservable(); - } - - public getVorgang(): VorgangWithEingangResource { - return this.vorgangWithEingang$.value.resource; - } - - private getVorgangWithEingangUrl(): ResourceUri { + getVorgangWithEingangUri(): ResourceUri { return this.navigationService.getDecodedParam(VorgangService.VORGANG_WITH_EINGANG_URL); } - loadVorgangWithEingang(vorgangUri: ResourceUri): void { - this.setStateResourceOnLoading(this.vorgangWithEingang$); - - this.vorgangRepository.getVorgang(vorgangUri).pipe(first()) - .subscribe(vorgang => this.updateVorgang(vorgang)); - } - - updateVorgang(vorgang: VorgangWithEingangResource): void { - this.setVorgangWithEingang(createStateResource(vorgang)); - - this.loadPendingCommandsByVorgang(vorgang); - } - - setVorgangWithEingang(stateResource: StateResource<VorgangWithEingangResource>) { - this.vorgangWithEingang$.next(stateResource); - } - - loadPendingCommandsByVorgang(vorgang: VorgangWithEingangResource): void { - if (this.hasPendingCommands(vorgang)) { - this.commandService.getPendingCommands(vorgang, VorgangWithEingangLinkRel.PENDING_COMMANDS).pipe(first()) - .subscribe(result => this.handlePendingCommands(result.resource)); - } - } - - private hasPendingCommands(vorgang: VorgangWithEingangResource): boolean { - return hasLink(vorgang, VorgangWithEingangLinkRel.PENDING_COMMANDS); - } - - handlePendingCommands(commandListResource: CommandListResource): void { - this.setPendingForwardCommand(commandListResource); - this.handlePendingSendPostfachMailCommand(commandListResource); + public getAttachments(): Observable<StateResource<BinaryFileListResource>> { + return this.facade.getAttachmentList().pipe( + withLatestFrom(this.facade.getVorgangWithEingang()), + tap(([attachmentList, vorgangWithEingang]) => doIfLoadingRequired(attachmentList, () => this.facade.loadAttachmentList(vorgangWithEingang.resource))), + map(([attachmentList, ]) => attachmentList), + startWith(createEmptyStateResource<BinaryFileListResource>(true))); } - setPendingForwardCommand(pendingCommandList: CommandListResource): void { - if (!this.pendingForwardCommand$.value.loaded) { - this.setPendingForwardSingleCommand(createStateResource(getPendingCommandByOrder(pendingCommandList, [VorgangOrder.FORWARD]))); - } + public getRepresentations(): Observable<StateResource<BinaryFileListResource>> { + return this.facade.getRepresentationList().pipe( + withLatestFrom(this.facade.getVorgangWithEingang()), + tap(([representationList, vorgangWithEingang]) => doIfLoadingRequired(representationList, () => this.facade.loadRepresentationList(vorgangWithEingang.resource))), + map(([representationList, ]) => representationList), + startWith(createEmptyStateResource<BinaryFileListResource>(true))); } - public setPendingForwardSingleCommand(pendingCommand: StateResource<CommandResource>): void { - this.pendingForwardCommand$.next(pendingCommand); - } - handlePendingSendPostfachMailCommand(pendingCommandList: CommandListResource): void { - if (!this.pendingSendPostfachMailCommand$.value.loaded) { - this.setPendingSendPostfachMailCommand(createStateResource(getPendingCommandByOrder(pendingCommandList, [PostfachOrder.SEND_POSTFACH_NACHRICHT]))); - } + public clearVorgang(): void { + this.facade.clearVorgangWithEingang(); } - public setPendingSendPostfachMailCommand(pendingCommand: StateResource<CommandResource>) { - this.pendingSendPostfachMailCommand$.next(pendingCommand); - } - public setPendingForwardSingleCommandLoading(): void { - this.pendingForwardCommand$.next(createEmptyStateResource(true)); + public setPendingSendPostfachMailCommand(command: StateResource<CommandResource>) { + this.facade.setPendingSendPostfachMailSingleCommand(command); } - public setPendingSendPostfachMailSingleCommandLoading(): void { - this.pendingSendPostfachMailCommand$.next(createEmptyStateResource(true)); - } - - public getPendingForwardCommand(): Observable<StateResource<CommandResource>> { - return this.pendingForwardCommand$.asObservable(); + this.facade.setPendingSendPostfachMailSingleCommandLoading(); } - public getPendingSendPostfachMailCommand(): Observable<StateResource<CommandResource>> { - return this.pendingSendPostfachMailCommand$.asObservable(); - } - - setStateResourceOnLoading(stateResource$: BehaviorSubject<StateResource<any>>): void { - stateResource$.next({ ...stateResource$.value, loading: true }); + return this.facade.getSendPostfachNachrichtPendingCommand(); } - public getAttachments(): Observable<StateResource<BinaryFileListResource>> { - return this.attachments$.asObservable().pipe( - tap(attachments => doIfLoadingRequired(attachments, () => this.loadAttachments())), - startWith(createEmptyStateResource<BinaryFileListResource>(true))); - } - - loadAttachments(): void { - this.setStateResourceOnLoading(this.attachments$); - - this.vorgangRepository.getAttachments(this.getVorgang()).pipe(first()) - .subscribe(result => this.attachments$.next(createStateResource(result))); - } - public getRepresentations(): Observable<StateResource<BinaryFileListResource>> { - return this.representations$.asObservable().pipe( - tap(representations => doIfLoadingRequired(representations, () => this.loadRepresentations())), - startWith(createEmptyStateResource<BinaryFileListResource>(true))); + public setPendingForwardSingleCommandLoading(): void { + this.facade.setForwardSingleCommandLoading(); } - - loadRepresentations(): void { - this.setStateResourceOnLoading(this.representations$); - - this.vorgangRepository.getRepresentations(this.getVorgang()).pipe(first()) - .subscribe(result => this.representations$.next(createStateResource(result))) + public getPendingForwardCommand(): Observable<StateResource<CommandResource>> { + return this.facade.getForwardPendingCommand(); } - - private listenForLeavingVorgang(): void { - if (isNotUndefined(this.navigationSubscription)) this.navigationSubscription.unsubscribe(); - this.navigationSubscription = this.navigationService.urlChanged().subscribe(params => this.onNavigation(params)); + public setPendingForwardSingleCommand(command: StateResource<CommandResource>): void { + this.facade.setForwardSingleCommand(command); } - onNavigation(params: Params): void { - if (NavigationService.isVorgangListPage(params)) { - this.clearVorgang(); - this.clearPendingForwardCommand(); - this.clearPendingSendPostfachMailCommand(); - this.clearAttachments(); - this.clearRepresentations(); - return; - } - if (NavigationService.isVorgangDetailPage(params, VorgangService.VORGANG_WITH_EINGANG_URL)) { - this.reloadCurrentVorgang(); - } - } public reloadCurrentVorgang(): void { - this.loadVorgangWithEingang(this.getVorgangWithEingangUrl()); - } - - public clearVorgang(): void { - this.vorgangWithEingang$.next(createEmptyStateResource<VorgangWithEingangResource>()); - } - - clearPendingForwardCommand(): void { - this.pendingForwardCommand$.next(createEmptyStateResource<CommandResource>()); - } - - clearPendingSendPostfachMailCommand(): void { - this.pendingSendPostfachMailCommand$.next(createEmptyStateResource<CommandResource>()); - } - - clearAttachments(): void { - this.attachments$.next(createEmptyStateResource<BinaryFileListResource>()); - } - - clearRepresentations(): void { - this.representations$.next(createEmptyStateResource<BinaryFileListResource>()); + this.facade.loadVorgangWithEingang(this.getVorgangWithEingangUri()); } public assignUser(userUri: ResourceUri): Observable<StateResource<CommandResource>> { - this.setAssignUserCommandLoading(); + this.facade.assignUser(); - return this.commandService.createCommand(this.getVorgang(), VorgangWithEingangLinkRel.ASSIGN, this.createAssignUserOrder(userUri)).pipe( + return this.facade.getVorgangWithEingang().pipe( + mergeMap(vorgangWithEingang => this.commandService.createCommand(vorgangWithEingang.resource, VorgangWithEingangLinkRel.ASSIGN, createAssignUserCommand(userUri))), tap(commandStateResource => this.reloadVorgangOnDone(commandStateResource)), - startWith(createEmptyStateResource<CommandResource>(true))) - } - - setAssignUserCommandLoading(): void { - this.assignUserCommand$.next({ ...this.assignUserCommand$.value, loading: true }); - } - - private createAssignUserOrder(assignedTo: ResourceUri): CreateAssignUserCommand { - return { order: VorgangOrder.ASSIGN_USER, body: { assignedTo } }; + startWith(createEmptyStateResource<CommandResource>(true))); } reloadVorgangOnDone(commandResource: StateResource<CommandResource>): void { @@ -254,18 +129,14 @@ export class VorgangService { } setAssignUserCommand(commandStateResource: StateResource<CommandResource>): void { - this.assignUserCommand$.next(commandStateResource); + this.facade.assignUserSuccess(commandStateResource); } public reloadVorgang(commandResource: CommandResource): void { - this.loadVorgangWithEingang(getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE)); + this.facade.loadVorgangWithEingang(getUrl(commandResource, CommandLinkRel.EFFECTED_RESOURCE)); } - // - public getBackButtonUrl(): Observable<string> { - return this.vorgangFacade.getBackButtonUrl(); + return this.facade.getBackButtonUrl(); } - - // } diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.spec.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.spec.ts index cabde8fc9b436a331b00b3bd015a83aa318dff7a..fd24b8facd759581ad934383862cb07e8bc2c7c2 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.spec.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.spec.ts @@ -21,11 +21,12 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ +import faker from '@faker-js/faker'; import { CreateCommand } from '@goofy-client/command-shared'; import { createVorgangForwardRequest, createVorgangListResource } from 'libs/vorgang-shared/test/vorgang'; import { VorgangListLinkRel } from './vorgang.linkrel'; import { ForwardRequest, VorgangOrder, VorgangResource } from './vorgang.model'; -import { createAbschliessenCommand, createAnnehmenCommand, createBearbeitenCommand, createBescheidenCommand, createForwardCommand, createVerwerfenCommand, createZurueckholenCommand, createZurueckstellenCommand, getVorgaengeFromList } from './vorgang.util'; +import { createAbschliessenCommand, createAnnehmenCommand, createAssignUserCommand, createBearbeitenCommand, createBescheidenCommand, createForwardCommand, createVerwerfenCommand, createZurueckholenCommand, createZurueckstellenCommand, getVorgaengeFromList } from './vorgang.util'; describe('VorgangUtil', () => { @@ -117,4 +118,15 @@ describe('VorgangUtil', () => { expect(result.length).toBe(10); }) }) + + describe('create createAssignUserCommand', () => { + + const userProfileUri: string = faker.internet.url(); + + it('should return command', () => { + const result: CreateCommand = createAssignUserCommand(userProfileUri); + + expect(result).toEqual({ order: VorgangOrder.ASSIGN_USER, body: { assignedTo: userProfileUri } }) + }) + }) }) \ No newline at end of file diff --git a/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.ts b/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.ts index b755f786ca4b1ceaa1e985e987716a59f74d6d16..6dc371b26cc63b5faefba21856811806f3f7383b 100644 --- a/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.ts +++ b/goofy-client/libs/vorgang-shared/src/lib/vorgang.util.ts @@ -23,10 +23,10 @@ */ import { CreateCommand } from '@goofy-client/command-shared'; import { EMPTY_ARRAY, isNotNil } from '@goofy-client/tech-shared'; -import { getEmbeddedResource } from '@ngxp/rest'; +import { ResourceUri, getEmbeddedResource } from '@ngxp/rest'; import { isNull } from 'lodash-es'; import { VorgangListLinkRel } from './vorgang.linkrel'; -import { CreateForwardCommand, ForwardRequest, VorgangListResource, VorgangOrder, VorgangResource } from './vorgang.model'; +import { CreateAssignUserCommand, CreateForwardCommand, ForwardRequest, VorgangListResource, VorgangOrder, VorgangResource } from './vorgang.model'; export function createZurueckholenCommand(): CreateCommand { return { order: VorgangOrder.ZURUECKHOLEN, body: null }; @@ -71,3 +71,7 @@ export function getVorgaengeFromList(vorgangList: VorgangListResource): VorgangR } return EMPTY_ARRAY; } + +export function createAssignUserCommand(assignedTo: ResourceUri): CreateAssignUserCommand { + return { order: VorgangOrder.ASSIGN_USER, body: { assignedTo } }; +} \ No newline at end of file diff --git a/goofy-client/package.json b/goofy-client/package.json index 6235e0f55445b95c1cac5508f4896c722c7a6038..56595217545b063679838adcca3d02a46fcfb806 100644 --- a/goofy-client/package.json +++ b/goofy-client/package.json @@ -33,8 +33,8 @@ "help": "nx help", "favicon": "real-favicon generate favicon/faviconDescription.json favicon/faviconData.json src/favicon", "cypress:run": "npx cypress run --project apps/goofy-e2e", - "cypress:run-main": "npx cypress run --project apps/goofy-e2e --config video=false integrationFolder=./src/integration/main-tests", - "cypress:run-ea": "npx cypress run --project apps/goofy-e2e --config video=false integrationFolder=./src/integration/einheitlicher-ansprechpartner", + "cypress:run-main": "npx cypress run --project apps/goofy-e2e --config video=false,integrationFolder=./src/integration/main-tests", + "cypress:run-ea": "npx cypress run --project apps/goofy-e2e --config video=false,integrationFolder=./src/integration/einheitlicher-ansprechpartner", "cypress:run-minimal": "npx cypress run --project apps/goofy-e2e --config video=false", "cypress:version": "npx cypress version", "cypress:install": "npx cypress install",