import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { isArray, isString, toArray } from '../utils'; import { IqserPermissions, PermissionValidationFn } from '../types'; import { List } from '../../utils'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root', }) export class IqserPermissionsService { readonly permissions$: Observable; readonly #permissions$ = new BehaviorSubject({}); constructor() { this.permissions$ = this.#permissions$.asObservable(); } clear(): void { this.#permissions$.next({}); } has(permission: string): boolean; has(permissions: List): boolean; has(permissions: string | List): boolean; has(permissions: string | List): boolean { return this.#evaluate(permissions).every(result => result); } hasSome(permissions: List | string): boolean { return this.#evaluate(permissions).some(result => result); } load(permissions: IqserPermissions | List) { return this.#reduce(permissions); } add(permissions: string | List | IqserPermissions) { const _permissions = isString(permissions) ? { [permissions]: () => true } : permissions; return this.#reduce(_permissions, this.#permissions$.value); } remove(permission: string) { const permissions = { ...this.#permissions$.value }; delete permissions[permission]; this.#permissions$.next(permissions); } get(): IqserPermissions; get(permission: string): PermissionValidationFn | undefined; get(permission?: string): IqserPermissions | PermissionValidationFn | undefined { return permission ? this.#permissions$.value[permission] : this.#permissions$.value; } has$(permission: string): Observable; has$(permissions: List): Observable; has$(permissions: string | List): Observable; has$(permissions: string | List) { const isEmpty = !permissions || permissions.length === 0; if (isEmpty) { return of(true); } return this.permissions$.pipe( map(all => toArray(permissions).map(permission => all[permission]?.(permission, all) ?? false)), map(results => results.every(result => result)), ); } #evaluate(permissions: List | string) { const isEmpty = !permissions || permissions.length === 0; if (isEmpty) { return [true]; } const all = this.#permissions$.value; return toArray(permissions).map(permission => all[permission]?.(permission, all) ?? false); } #reduce(permissions: IqserPermissions | List, initialValue = {} as IqserPermissions) { if (isArray(permissions)) { return this.#permissions$.next(this.#reduceList(permissions, initialValue)); } return this.#permissions$.next(this.#reduceObject(permissions, initialValue)); } #reduceObject(permissions: IqserPermissions, initialValue: IqserPermissions = {}) { return Object.entries(permissions).reduce((acc, [permission, validationFn]) => { return { ...acc, [permission]: validationFn }; }, initialValue); } #reduceList(permissions: List, initialValue: IqserPermissions = {}): IqserPermissions { return permissions.reduce((acc, permission) => { return { ...acc, [permission]: () => true }; }, initialValue); } }