Select Git revision
keycloak.resource.service.ts
-
Albert Bruns authoredAlbert Bruns authored
keycloak.resource.service.ts 3.73 KiB
/*
* Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den
* Ministerpräsidenten des Landes Schleswig-Holstein
* Staatskanzlei
* Abteilung Digitalisierung und zentrales IT-Management der Landesregierung
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { createEmptyStateResource, createStateResource, doIfLoadingRequired, StateResource } from '@alfa-client/tech-shared';
import { BehaviorSubject, catchError, first, map, Observable, startWith, switchMap, tap, throwError } from 'rxjs';
export abstract class KeycloakResourceService<T> {
readonly stateResource: BehaviorSubject<StateResource<T[]>> = new BehaviorSubject(createEmptyStateResource());
public getAll(): Observable<StateResource<T[]>> {
return this.stateResource
.asObservable()
.pipe(switchMap((stateResource: StateResource<T[]>) => this._handleChanges(stateResource)));
}
_handleChanges(stateResource: StateResource<T[]>): Observable<StateResource<T[]>> {
doIfLoadingRequired(stateResource, (): void => this._loadResource());
return this.stateResource.asObservable();
}
_loadResource(): void {
this._setLoading();
this._getItemsFromKeycloak()
.pipe(first())
.subscribe((items: T[]): void => this.updateResource(items));
}
protected abstract _getItemsFromKeycloak(): Observable<T[]>;
private updateResource(items: T[]): void {
this.stateResource.next(createStateResource(items));
}
public create(item: Partial<T>): Observable<StateResource<T>> {
return this._handleLoading<T>(this._createInKeycloak(item));
}
protected abstract _createInKeycloak(item: Partial<T>): Observable<T>;
public save(item: T): Observable<StateResource<T>> {
return this._handleLoading<T>(this._saveInKeycloak(item));
}
protected abstract _saveInKeycloak(item: T): Observable<T>;
public delete(id: string): Observable<StateResource<void>> {
return this._handleLoading(this._deleteInKeycloak(id));
}
protected abstract _deleteInKeycloak(id: string): Observable<void>;
_handleLoading<D>(action: Observable<D>): Observable<StateResource<D>> {
this._setLoading();
return this._setLoadingInStateResource<D>(this._refreshAfterEmit<D>(action));
}
_refreshAfterEmit<D>(action: Observable<D>): Observable<D> {
return action.pipe(
first(),
tap((): void => this.refresh()),
catchError((err: Error) => this.handleError(err)),
);
}
handleError(err: Error): Observable<never> {
this.refresh();
return throwError(() => err);
}
_setLoadingInStateResource<D>(action: Observable<D>): Observable<StateResource<D>> {
return action.pipe(
map((value: D): StateResource<D> => createStateResource<D>(value)),
startWith(createEmptyStateResource<D>(true)),
);
}
_setLoading(): void {
this.stateResource.next({ ...this.stateResource.value, loading: true });
}
refresh(): void {
this.stateResource.next({
...createStateResource<T[]>([]),
reload: true,
});
}
public selectResource(): Observable<StateResource<T[]>> {
return this.stateResource.asObservable();
}
}