From 0eacadc3f4f4387dabfaca0e4630ad94b2466a4c Mon Sep 17 00:00:00 2001 From: OZGCloud <ozgcloud@mgm-tp.com> Date: Wed, 2 Oct 2024 17:12:20 +0200 Subject: [PATCH] OZG-6731 multiple stateResources --- ...anisationseinheit-container.component.html | 10 +- ...rganisationseinheit-container.component.ts | 7 +- .../organisationseinheit-form.component.html | 67 ++++++------ ...rganisationseinheit-form.component.spec.ts | 48 +++------ .../organisationseinheit-form.component.ts | 12 +-- .../organisationseinheit.formservice.ts | 30 +++--- .../user/keycloak.resource.service.spec.ts | 31 +++--- .../src/lib/user/keycloak.resource.service.ts | 102 ++++++++++-------- 8 files changed, 143 insertions(+), 164 deletions(-) diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.html b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.html index 3e7af75f49..973b18c636 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.html +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.html @@ -13,10 +13,12 @@ label="Neue Organisationseinheit anlegen" > </admin-secondary-button> - <admin-spinner - data-test-id="organisationseinheit-spinner" - *ngIf="deleteInProgress$ | async" - ></admin-spinner> + <ng-container *ngIf="deleteStateResource$ | async as deleteStateResource"> + <admin-spinner + data-test-id="organisationseinheit-spinner" + *ngIf="deleteStateResource.loading" + ></admin-spinner> + </ng-container> <admin-organisationseinheit-list [organisationseinheitItems]="organisationseinheitItems" diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.ts index b85e0c8447..7ae9fa331f 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.ts +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-container.component.ts @@ -1,4 +1,4 @@ -import { StateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, StateResource } from '@alfa-client/tech-shared'; import { Component, OnInit, ViewChild } from '@angular/core'; import { Observable, of } from 'rxjs'; import { Organisationseinheit } from '../../user/user.model'; @@ -11,7 +11,8 @@ import { OrganisationseinheitFormComponent } from './organisationseinheit-form/o }) export class OrganisationseinheitContainerComponent implements OnInit { organisationseinheitItems$: Observable<StateResource<Organisationseinheit[]>>; - deleteInProgress$: Observable<boolean> = of(false); + + deleteStateResource$: Observable<StateResource<string>> = of(createEmptyStateResource<string>()); @ViewChild(OrganisationseinheitFormComponent) private form!: OrganisationseinheitFormComponent; @@ -31,6 +32,6 @@ export class OrganisationseinheitContainerComponent implements OnInit { } public delete(organisationseinheit: Organisationseinheit): void { - this.deleteInProgress$ = this.organisationseinheitService.delete(organisationseinheit.id); + this.deleteStateResource$ = this.organisationseinheitService.delete(organisationseinheit.id); } } diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.html b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.html index 3bd257438d..12eae70179 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.html +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.html @@ -1,34 +1,35 @@ -<ng-container *ngIf="submitInProgress$ | async"></ng-container> -<dialog #OrganisationseinheitDialog data-test-id="organisationseinheit-dialog" class="bg-gray-50"> - <button - (click)="OrganisationseinheitDialog.close()" - data-test-id="organisationseinheit-close-button" - class="absolute right-3 top-1 text-2xl text-black hover:font-bold active:text-black/80" - > - ✕ - </button> - <form [formGroup]="formService.form" class="m-5 grid grid-cols-1 gap-5"> - <h1 class="text-2xl" data-test-id="organisationseinheit-form-header"> - {{ label }} - </h1> - <text-field - label="Name" - data-test-id="organisationseinheit-name" - [formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_NAME_FIELD" - ></text-field> - <text-field - label="OrganisationseinheitID" - data-test-id="organisationseinheit-id" - [formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_IDS_FIELD" - ></text-field> - - <ods-button-with-spinner - data-test-id="organisationseinheit-save-button" - class="justify-self-end" - (clickEmitter)="submit()" - [stateResource]="organisationseinheitItems" - text="Speichern" +<ng-container *ngIf="submitStateResource$ | async as submitStateResource"> + <dialog #OrganisationseinheitDialog data-test-id="organisationseinheit-dialog" class="bg-gray-50"> + <button + (click)="OrganisationseinheitDialog.close()" + data-test-id="organisationseinheit-close-button" + class="absolute right-3 top-1 text-2xl text-black hover:font-bold active:text-black/80" > - </ods-button-with-spinner> - </form> -</dialog> + ✕ + </button> + <form [formGroup]="formService.form" class="m-5 grid grid-cols-1 gap-5"> + <h1 class="text-2xl" data-test-id="organisationseinheit-form-header"> + {{ label }} + </h1> + <text-field + label="Name" + data-test-id="organisationseinheit-name" + [formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_NAME_FIELD" + ></text-field> + <text-field + label="OrganisationseinheitID" + data-test-id="organisationseinheit-id" + [formControlName]="OrganisationseinheitFormService.ORGANISATIONSEINHEIT_IDS_FIELD" + ></text-field> + + <ods-button-with-spinner + data-test-id="organisationseinheit-save-button" + class="justify-self-end" + (clickEmitter)="submit()" + [stateResource]="submitStateResource" + text="Speichern" + > + </ods-button-with-spinner> + </form> + </dialog> +</ng-container> diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts index 55b944fec8..b45d0b36c7 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.spec.ts @@ -8,12 +8,7 @@ import { } from '@alfa-client/test-utils'; import { DebugElement } from '@angular/core'; import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { - AbstractControl, - FormsModule, - ReactiveFormsModule, - UntypedFormGroup, -} from '@angular/forms'; +import { AbstractControl, FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms'; import { ButtonWithSpinnerComponent } from '@ods/component'; import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; import { MockComponent, ngMocks } from 'ng-mocks'; @@ -30,9 +25,7 @@ describe('OrganisationseinheitFormComponent', () => { let fixture: ComponentFixture<OrganisationseinheitFormComponent>; let form: UntypedFormGroup; - const organisationseinheitService: Mock<OrganisationseinheitService> = mock( - OrganisationseinheitService, - ); + const organisationseinheitService: Mock<OrganisationseinheitService> = mock(OrganisationseinheitService); const saveButtonSelector: string = getDataTestIdOf('organisationseinheit-save-button'); const closeButtonSelector: string = getDataTestIdOf('organisationseinheit-close-button'); @@ -64,34 +57,20 @@ describe('OrganisationseinheitFormComponent', () => { describe('form element', () => { const fields: string[][] = [ - [ - OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_NAME_FIELD, - 'Name', - 'organisationseinheit-name', - ], - [ - OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_IDS_FIELD, - 'OrganisationseinheitID', - 'organisationseinheit-id', - ], + [OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_NAME_FIELD, 'Name', 'organisationseinheit-name'], + [OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_IDS_FIELD, 'OrganisationseinheitID', 'organisationseinheit-id'], ]; - it.each(fields)( - 'should have label for field "%s" with name "%s"', - (fieldName: string, text: string, inputId: string) => { - const textFieldElement = getElementFromFixture(fixture, getDataTestIdOf(inputId)); - expect(textFieldElement.getAttribute('label')).toBe(text); - }, - ); + it.each(fields)('should have label for field "%s" with name "%s"', (fieldName: string, text: string, inputId: string) => { + const textFieldElement = getElementFromFixture(fixture, getDataTestIdOf(inputId)); + expect(textFieldElement.getAttribute('label')).toBe(text); + }); it.each(fields)('should bind form for text-field "%s"', (fieldName, text, dataTestId) => { const fieldValue: string = `some text-field ${text}`; const formControl: AbstractControl = form.get(fieldName); - const textFieldComponent: DebugElement = getDebugElementFromFixtureByCss( - fixture, - getDataTestIdOf(dataTestId), - ); + const textFieldComponent: DebugElement = getDebugElementFromFixtureByCss(fixture, getDataTestIdOf(dataTestId)); ngMocks.change(textFieldComponent, fieldValue); expect(formControl.value).toBe(fieldValue); }); @@ -101,10 +80,7 @@ describe('OrganisationseinheitFormComponent', () => { let saveButtonComponent: ButtonWithSpinnerComponent; beforeEach(() => { - saveButtonComponent = getDebugElementFromFixtureByCss( - fixture, - saveButtonSelector, - ).componentInstance; + saveButtonComponent = getDebugElementFromFixtureByCss(fixture, saveButtonSelector).componentInstance; }); it('should call submit on click', () => { @@ -141,7 +117,7 @@ describe('OrganisationseinheitFormComponent', () => { component.formService.submit = () => throwError(() => new Error('some error')); component.submit(); - component.submitInProgress$.subscribe({ + component.submitStateResource$.subscribe({ error: () => {}, }); tick(); @@ -153,7 +129,7 @@ describe('OrganisationseinheitFormComponent', () => { component.formService.submit = () => of(false); component.submit(); - component.submitInProgress$.subscribe(); + component.submitStateResource$.subscribe(); tick(); expect(component.handleProgressChange).toHaveBeenCalled(); diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.ts index 9796922d5e..13c41df783 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.ts +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit-form.component.ts @@ -1,4 +1,4 @@ -import { StateResource, createStateResource } from '@alfa-client/tech-shared'; +import { StateResource, createEmptyStateResource, createStateResource } from '@alfa-client/tech-shared'; import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { Observable, of, tap } from 'rxjs'; import { Organisationseinheit } from '../../../user/user.model'; @@ -21,7 +21,7 @@ export class OrganisationseinheitFormComponent implements AfterViewInit { @ViewChild('OrganisationseinheitDialog') private dialogRef: ElementRef<HTMLDialogElement>; dialog: HTMLDialogElement; - submitInProgress$: Observable<boolean> = of(false); + submitStateResource$: Observable<StateResource<Organisationseinheit>> = of(createEmptyStateResource<Organisationseinheit>()); label: string; @@ -32,13 +32,13 @@ export class OrganisationseinheitFormComponent implements AfterViewInit { } public submit() { - this.submitInProgress$ = this.formService + this.submitStateResource$ = this.formService .submit() - .pipe(tap((progress: boolean) => this.handleProgressChange(progress))); + .pipe(tap((stateResource: StateResource<Organisationseinheit>) => this.handleProgressChange(stateResource))); } - handleProgressChange(progress: boolean): void { - if (!progress) { + handleProgressChange(stateResource: StateResource<Organisationseinheit>): void { + if (!stateResource.loading) { this.completeIfNoErrors(); } } diff --git a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit.formservice.ts b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit.formservice.ts index 3309b9a813..127b99ff73 100644 --- a/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit.formservice.ts +++ b/alfa-client/libs/admin/settings/src/lib/organisationseinheit/organisationseinheit-container/organisationseinheit-form/organisationseinheit.formservice.ts @@ -1,12 +1,8 @@ -import { createStateResource, isNotNil, StateResource } from '@alfa-client/tech-shared'; +import { createEmptyStateResource, createStateResource, isNotNil, StateResource } from '@alfa-client/tech-shared'; import { Injectable, Input } from '@angular/core'; import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { catchError, Observable, of } from 'rxjs'; -import { - Organisationseinheit, - OrganisationseinheitError, - OrganisationseinheitErrorType, -} from '../../../user/user.model'; +import { catchError, Observable, of, tap } from 'rxjs'; +import { Organisationseinheit, OrganisationseinheitError, OrganisationseinheitErrorType } from '../../../user/user.model'; import { getOrganisationseinheitErrorMessage } from '../../../user/user.util'; import { OrganisationseinheitService } from '../../organisationseinheitService'; @@ -32,31 +28,32 @@ export class OrganisationseinheitFormservice { }); } - public submit(): Observable<boolean> { + public submit(): Observable<StateResource<Organisationseinheit>> { if (this.validate()) { return this.callService().pipe( + tap((state) => console.log(state)), catchError((error: OrganisationseinheitError) => { this.handleError(error); - return of(false); + return of(createEmptyStateResource<Organisationseinheit>()); }), ); } else { - return of(false); + return of(createEmptyStateResource<Organisationseinheit>()); } } - callService(): Observable<boolean> { + callService(): Observable<StateResource<Organisationseinheit>> { return this.isPatch() ? this.save() : this.create(); } - create(): Observable<boolean> { + create(): Observable<StateResource<Organisationseinheit>> { return this.organisationsEinheitService.create({ name: this.getName(), organisationseinheitIds: this.getOrganisationseinheitIds(), }); } - save(): Observable<boolean> { + save(): Observable<StateResource<Organisationseinheit>> { return this.organisationsEinheitService.save({ ...this.source, name: this.getName(), @@ -79,9 +76,7 @@ export class OrganisationseinheitFormservice { } private getName(): string { - return this.getStringFromPotentiallyEmptyField( - OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_NAME_FIELD, - ); + return this.getStringFromPotentiallyEmptyField(OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_NAME_FIELD); } private getOrganisationseinheitIds(): string[] { @@ -126,8 +121,7 @@ export class OrganisationseinheitFormservice { this.source = organisationseinheit; this.form.patchValue({ [OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_NAME_FIELD]: organisationseinheit.name, - [OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_IDS_FIELD]: - organisationseinheit.organisationseinheitIds.join(', '), + [OrganisationseinheitFormservice.ORGANISATIONSEINHEIT_IDS_FIELD]: organisationseinheit.organisationseinheitIds.join(', '), }); } diff --git a/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.spec.ts b/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.spec.ts index ee89610514..540130a154 100644 --- a/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.spec.ts +++ b/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.spec.ts @@ -1,10 +1,7 @@ import { fakeAsync, tick } from '@angular/core/testing'; import faker from '@faker-js/faker'; import { cold } from 'jest-marbles'; -import { - StateResource, - createEmptyStateResource, -} from 'libs/tech-shared/src/lib/resource/resource.util'; +import { StateResource, createEmptyStateResource } from 'libs/tech-shared/src/lib/resource/resource.util'; import { createDummy } from 'libs/tech-shared/test/dummy'; import { singleCold } from 'libs/tech-shared/test/marbles'; import { Observable, of } from 'rxjs'; @@ -30,7 +27,7 @@ describe('KeycloakResourceService', () => { it('should return stateResource as observable', (done) => { service.get().subscribe((stateResource) => { - expect(stateResource).toBe(service.stateResource.value); + expect(stateResource).toBe(service.listStateResource.value); done(); }); }); @@ -68,7 +65,7 @@ describe('KeycloakResourceService', () => { service.loadResource(); tick(); - expect(service.stateResource.value.resource).toEqual(dummyItems); + expect(service.listStateResource.value.resource).toEqual(dummyItems); })); }); @@ -132,7 +129,7 @@ describe('KeycloakResourceService', () => { it('should set loading', () => { service.handleLoading(dummyAction); - expect(service.stateResource.value.loading).toBe(true); + expect(service.listStateResource.value.loading).toBe(true); }); it('should call refreshAfterFirstEmit', () => { @@ -154,12 +151,12 @@ describe('KeycloakResourceService', () => { describe('refreshAfterFirstEmit', () => { it('should call refresh after first emit', fakeAsync(() => { - service.refresh = jest.fn(); + service.refreshList = jest.fn(); service.refreshAfterFirstEmit(dummyAction).subscribe(); tick(); - expect(service.refresh).toHaveBeenCalled(); + expect(service.refreshList).toHaveBeenCalled(); })); }); @@ -175,36 +172,36 @@ describe('KeycloakResourceService', () => { it('should set loading in state to true without parameter', () => { service.setLoading(); - expect(service.stateResource.value.loading).toEqual(true); + expect(service.listStateResource.value.loading).toEqual(true); }); it('should set loading in state to false for parameter false', () => { service.setLoading(false); - expect(service.stateResource.value.loading).toEqual(false); + expect(service.listStateResource.value.loading).toEqual(false); }); }); describe('refresh', () => { it('should set reload in state to true', () => { - service.refresh(); + service.refreshList(); - expect(service.stateResource.value.reload).toBe(true); + expect(service.listStateResource.value.reload).toBe(true); }); it('should clear resource in state', () => { - service.refresh(); + service.refreshList(); - expect(service.stateResource.value.resource).toBe(null); + expect(service.listStateResource.value.resource).toBe(null); }); }); describe('select resource', () => { it('should return state resource', () => { const stateResource = createEmptyStateResource<unknown[]>(); - service.stateResource.next(stateResource); + service.listStateResource.next(stateResource); - const resource$: Observable<StateResource<unknown[]>> = service.selectResource(); + const resource$: Observable<StateResource<unknown[]>> = service.selectListResource(); expect(resource$).toBeObservable(singleCold(stateResource)); }); diff --git a/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.ts b/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.ts index 914d547f4a..ed608c73a9 100644 --- a/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.ts +++ b/alfa-client/libs/admin/settings/src/lib/user/keycloak.resource.service.ts @@ -1,20 +1,14 @@ -import { - createEmptyStateResource, - createStateResource, - doIfLoadingRequired, - StateResource, -} from '@alfa-client/tech-shared'; -import { BehaviorSubject, first, map, Observable, startWith, tap } from 'rxjs'; +import { createEmptyStateResource, createStateResource, doIfLoadingRequired, StateResource } from '@alfa-client/tech-shared'; +import { BehaviorSubject, first, Observable, startWith, switchMap, tap } from 'rxjs'; export abstract class KeycloakResourceService<T> { - readonly stateResource: BehaviorSubject<StateResource<T[]>> = new BehaviorSubject( - createEmptyStateResource(), - ); + readonly listStateResource: BehaviorSubject<StateResource<T[]>> = new BehaviorSubject(createEmptyStateResource()); + readonly createStateResource: BehaviorSubject<StateResource<T>> = new BehaviorSubject(createEmptyStateResource()); + readonly deleteStateResource: BehaviorSubject<StateResource<string>> = new BehaviorSubject(createEmptyStateResource()); + readonly saveStateResource: BehaviorSubject<StateResource<T>> = new BehaviorSubject(createEmptyStateResource()); public get() { - return this.stateResource - .asObservable() - .pipe(tap((stateResource) => this.handleChanges(stateResource))); + return this.listStateResource.asObservable().pipe(tap((stateResource) => this.handleChanges(stateResource))); } handleChanges(stateResource: StateResource<T[]>) { @@ -22,70 +16,84 @@ export abstract class KeycloakResourceService<T> { } loadResource(): void { - this.setLoading(); + this.setListLoading(this.listStateResource); this.getItemsFromKeycloak() .pipe(first()) .subscribe((items) => this.updateResource(items)); } + setListLoading(stateResource: BehaviorSubject<StateResource<unknown>>, loading: boolean = true): void { + stateResource.next({ + ...stateResource.value, + loading, + }); + } + abstract getItemsFromKeycloak(): Observable<T[]>; private updateResource(items: T[]): void { - this.stateResource.next(createStateResource(items)); + this.listStateResource.next(createStateResource(items)); + } + + public create(item: Partial<T>): Observable<StateResource<T>> { + this.createStateResource.next(createEmptyStateResource(true)); + + return this.createInKeycloak(item).pipe( + tap((item) => this.updateCreateStateResource(item)), + tap(() => this.refreshList()), + switchMap(() => this.createStateResource.asObservable()), + startWith(this.createStateResource.value), + ); } - public create(item: Partial<T>): Observable<boolean> { - return this.handleLoading(this.createInKeycloak(item)); + updateCreateStateResource(item: T): void { + this.createStateResource.next(createStateResource(item)); } abstract createInKeycloak(item: Partial<T>): Observable<T>; - public save(item: T): Observable<boolean> { - return this.handleLoading(this.saveInKeycloak(item)); - } + public save(item: T): Observable<StateResource<T>> { + this.saveStateResource.next(createEmptyStateResource(true)); - abstract saveInKeycloak(item: T): Observable<void>; + return this.saveInKeycloak(item).pipe( + tap(() => this.updateSaveStateResource(item)), + tap(() => this.refreshList()), + switchMap(() => this.saveStateResource.asObservable()), + startWith(this.saveStateResource.value), + ); + } - public delete(id: string): Observable<boolean> { - return this.handleLoading(this.deleteInKeycloak(id)); + updateSaveStateResource(item: T): void { + this.saveStateResource.next(createStateResource(item)); } - abstract deleteInKeycloak(id: string): Observable<void>; + abstract saveInKeycloak(item: T): Observable<void>; - handleLoading(action: Observable<unknown>): Observable<boolean> { - this.setLoading(); - return this.progress(this.refreshAfterFirstEmit(action)); - } + public delete(id: string): Observable<StateResource<string>> { + this.deleteStateResource.next(createEmptyStateResource(true)); - refreshAfterFirstEmit(action: Observable<unknown>): Observable<unknown> { - return action.pipe( - first(), - tap(() => this.refresh()), + return this.deleteInKeycloak(id).pipe( + tap(() => this.updateDeleteStateResource(id)), + tap(() => this.refreshList()), + switchMap(() => this.deleteStateResource.asObservable()), + startWith(this.deleteStateResource.value), ); } - progress(action: Observable<unknown>): Observable<boolean> { - return action.pipe( - map(() => false), - startWith(true), - ); + updateDeleteStateResource(id: string): void { + this.deleteStateResource.next(createStateResource(id)); } - setLoading(loading: boolean = true): void { - this.stateResource.next({ - ...this.stateResource.value, - loading, - }); - } + abstract deleteInKeycloak(id: string): Observable<void>; - refresh(): void { - this.stateResource.next({ + refreshList(): void { + this.listStateResource.next({ ...createEmptyStateResource(), reload: true, }); } - public selectResource(): Observable<StateResource<T[]>> { - return this.stateResource.asObservable(); + public selectListResource(): Observable<StateResource<T[]>> { + return this.listStateResource.asObservable(); } } -- GitLab