From d0551742ecfce6a473ab1431ce890372b1b1a40f Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Thu, 18 May 2023 12:19:53 +0300 Subject: [PATCH] RED-6713 remove tenants context holder --- .../services/permissions-guard.service.ts | 8 +++--- src/lib/tenants/services/index.ts | 1 - .../services/keycloak-status.service.ts | 6 ++-- .../tenants/services/tenant-context-holder.ts | 11 -------- .../tenants/services/tenant-id-interceptor.ts | 8 +++--- src/lib/tenants/services/tenants.service.ts | 28 ++++++++++--------- src/lib/tenants/tenant.pipe.ts | 6 ++-- .../users/guards/iqser-auth-guard.service.ts | 19 +++++++------ .../users/guards/iqser-role-guard.service.ts | 6 ++-- 9 files changed, 43 insertions(+), 50 deletions(-) delete mode 100644 src/lib/tenants/services/tenant-context-holder.ts diff --git a/src/lib/permissions/services/permissions-guard.service.ts b/src/lib/permissions/services/permissions-guard.service.ts index e10c512..51875a3 100644 --- a/src/lib/permissions/services/permissions-guard.service.ts +++ b/src/lib/permissions/services/permissions-guard.service.ts @@ -17,7 +17,7 @@ import { IqserPermissionsService } from './permissions.service'; import { IqserRolesService } from './roles.service'; import { isArray, isFunction, isRedirectWithParameters, isString, transformPermission } from '../utils'; import { List } from '../../utils'; -import { TenantContextHolder } from '../../tenants'; +import { TenantsService } from '../../tenants'; export interface IqserPermissionsData { readonly allow: string | List; @@ -30,7 +30,7 @@ export interface IqserPermissionsData { export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivateChild { constructor( private readonly _permissionsService: IqserPermissionsService, - private readonly _tenantContextHolder: TenantContextHolder, + private readonly _tenantsService: TenantsService, private readonly _rolesService: IqserRolesService, private readonly _router: Router, ) {} @@ -85,10 +85,10 @@ export class IqserPermissionsGuard implements CanActivate, CanMatch, CanActivate } if (Array.isArray(_redirectTo)) { - return this._router.navigate([this._tenantContextHolder.currentTenant, ..._redirectTo]); + return this._router.navigate([this._tenantsService.currentTenant, ..._redirectTo]); } - return this._router.navigate([`${this._tenantContextHolder.currentTenant}${_redirectTo}`]); + return this._router.navigate([`${this._tenantsService.currentTenant}${_redirectTo}`]); } #checkRedirect(permissions: IqserPermissionsData, route: ActivatedRouteSnapshot | Route, state?: RouterStateSnapshot) { diff --git a/src/lib/tenants/services/index.ts b/src/lib/tenants/services/index.ts index eb0cded..b609a0f 100644 --- a/src/lib/tenants/services/index.ts +++ b/src/lib/tenants/services/index.ts @@ -1,4 +1,3 @@ -export * from './tenant-context-holder'; export * from './tenants.service'; export * from './tenant-id-interceptor'; export * from './tenant-id-response-interceptor'; diff --git a/src/lib/tenants/services/keycloak-status.service.ts b/src/lib/tenants/services/keycloak-status.service.ts index 3459f27..1b49cf2 100644 --- a/src/lib/tenants/services/keycloak-status.service.ts +++ b/src/lib/tenants/services/keycloak-status.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from '@angular/core'; import { KeycloakService } from 'keycloak-angular'; import { getConfig } from '../../services'; -import { TenantContextHolder } from '../index'; +import { TenantsService } from '../index'; import { BASE_HREF } from '../../utils'; import { NGXLogger } from 'ngx-logger'; @@ -9,7 +9,7 @@ import { NGXLogger } from 'ngx-logger'; export class KeycloakStatusService { readonly #keycloakService = inject(KeycloakService); readonly #config = getConfig(); - readonly #tenantContextHolder = inject(TenantContextHolder); + readonly #tenantsService = inject(TenantsService); readonly #baseHref = inject(BASE_HREF); readonly #logger = inject(NGXLogger); @@ -30,7 +30,7 @@ export class KeycloakStatusService { } createLoginUrl() { - const currentTenant = this.#tenantContextHolder.currentTenant; + const currentTenant = this.#tenantsService.currentTenant; if (currentTenant && window.location.href.indexOf('/' + currentTenant) > 0) { return window.location.href; } diff --git a/src/lib/tenants/services/tenant-context-holder.ts b/src/lib/tenants/services/tenant-context-holder.ts deleted file mode 100644 index dafb467..0000000 --- a/src/lib/tenants/services/tenant-context-holder.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { TenantsService } from './tenants.service'; - -@Injectable({ providedIn: 'root' }) -export class TenantContextHolder { - readonly #tenantsService = inject(TenantsService); - - get currentTenant() { - return this.#tenantsService.currentTenant; - } -} diff --git a/src/lib/tenants/services/tenant-id-interceptor.ts b/src/lib/tenants/services/tenant-id-interceptor.ts index 4191380..ab5b242 100644 --- a/src/lib/tenants/services/tenant-id-interceptor.ts +++ b/src/lib/tenants/services/tenant-id-interceptor.ts @@ -1,16 +1,16 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { TenantContextHolder } from './tenant-context-holder'; +import { TenantsService } from './tenants.service'; @Injectable() export class TenantIdInterceptor implements HttpInterceptor { - protected readonly _tenantContext = inject(TenantContextHolder); + protected readonly _tenantsService = inject(TenantsService); intercept(req: HttpRequest, next: HttpHandler): Observable> { - if (this._tenantContext.currentTenant) { + if (this._tenantsService.currentTenant) { const updatedRequest = req.clone({ - setHeaders: { 'X-TENANT-ID': this._tenantContext.currentTenant }, + setHeaders: { 'X-TENANT-ID': this._tenantsService.currentTenant }, }); return next.handle(updatedRequest); diff --git a/src/lib/tenants/services/tenants.service.ts b/src/lib/tenants/services/tenants.service.ts index 0fa0774..b95c6a0 100644 --- a/src/lib/tenants/services/tenants.service.ts +++ b/src/lib/tenants/services/tenants.service.ts @@ -1,6 +1,6 @@ -import { inject, Injectable } from '@angular/core'; +import { computed, inject, Injectable, signal } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { BehaviorSubject, firstValueFrom } from 'rxjs'; +import { firstValueFrom } from 'rxjs'; import { NGXLogger } from 'ngx-logger'; import { BASE_HREF } from '../../utils'; import { Router } from '@angular/router'; @@ -13,8 +13,11 @@ export interface IBaseTenant { @Injectable({ providedIn: 'root' }) export class TenantsService { - hasMultipleTenants = false; - readonly tenantData$ = new BehaviorSubject(undefined); + readonly tenants = signal(undefined); + readonly hasMultiple = computed(() => { + const tenants = this.tenants(); + return tenants ? tenants.length > 1 : false; + }); readonly #http = inject(HttpClient); readonly #router = inject(Router); readonly #logger = inject(NGXLogger); @@ -27,17 +30,16 @@ export class TenantsService { removeItem: localStorage.removeItem.bind(localStorage), key: localStorage.key.bind(localStorage), }; - readonly #activeTenantId$ = new BehaviorSubject(''); + readonly #activeTenantId = signal(''); get currentTenant() { - return this.#activeTenantId$.value; + return this.#activeTenantId(); } async loadTenants() { this.#logger.info('[TENANTS] Loading tenants...'); const tenants = await firstValueFrom(this.#http.get('/tenants/simple')); - this.hasMultipleTenants = tenants.length > 1; - this.tenantData$.next(tenants); + this.tenants.set(tenants); const tenant = this.getTenantFromRoute(); if (tenant) { @@ -50,7 +52,7 @@ export class TenantsService { this.#logger.info('[TENANTS] No tenant in route'); - if (!this.hasMultipleTenants) { + if (!this.hasMultiple) { this.#logger.info('[TENANTS] Only one tenant loaded, auto-select it and redirect to login page'); const tenant = tenants[0].tenantId; if (await this.selectTenant(tenant)) { @@ -66,7 +68,7 @@ export class TenantsService { } async selectTenant(tenantId: string) { - const tenants = this.tenantData$.value; + const tenants = this.tenants(); if (!tenants) { throw new Error('Tenants not loaded!'); } @@ -80,13 +82,13 @@ export class TenantsService { } this.#mutateStorage(tenantId); - this.setCurrentTenantId(tenantId); + this.#setCurrentTenantId(tenantId); return true; } - setCurrentTenantId(tenantId: string) { + #setCurrentTenantId(tenantId: string) { this.#logger.info('[TENANTS] Set current tenant id: ', tenantId); - this.#activeTenantId$.next(tenantId); + this.#activeTenantId.set(tenantId); } #mutateStorage(tenant: string) { diff --git a/src/lib/tenants/tenant.pipe.ts b/src/lib/tenants/tenant.pipe.ts index 4f20fbf..5a13295 100644 --- a/src/lib/tenants/tenant.pipe.ts +++ b/src/lib/tenants/tenant.pipe.ts @@ -1,5 +1,5 @@ import { inject, Pipe, PipeTransform } from '@angular/core'; -import { TenantContextHolder } from './services'; +import { TenantsService } from './services'; @Pipe({ name: 'tenant', @@ -7,13 +7,13 @@ import { TenantContextHolder } from './services'; standalone: true, }) export class TenantPipe implements PipeTransform { - readonly #tenant = inject(TenantContextHolder); + readonly #tenantsService = inject(TenantsService); transform(value: string | string[]): string | undefined { if (!value) { return undefined; } const _value = Array.isArray(value) ? value.join('/') : value; - return '/' + this.#tenant.currentTenant + _value; + return '/' + this.#tenantsService.currentTenant + _value; } } diff --git a/src/lib/users/guards/iqser-auth-guard.service.ts b/src/lib/users/guards/iqser-auth-guard.service.ts index 4045c86..413557b 100644 --- a/src/lib/users/guards/iqser-auth-guard.service.ts +++ b/src/lib/users/guards/iqser-auth-guard.service.ts @@ -1,18 +1,20 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular'; -import { IqserConfigService } from '../../services'; +import { getConfig } from '../../services'; import { IqserUserService } from '../services/iqser-user.service'; -import { TenantContextHolder } from '../../tenants'; +import { TenantsService } from '../../tenants'; +import { KeycloakLoginOptions } from 'keycloak-js'; @Injectable() export class IqserAuthGuard extends KeycloakAuthGuard { + readonly #config = getConfig(); + constructor( protected readonly _router: Router, protected readonly _keycloak: KeycloakService, - private readonly _configService: IqserConfigService, private readonly _userService: IqserUserService, - private readonly _tenantContextHolder: TenantContextHolder, + private readonly _tenantsService: TenantsService, ) { super(_router, _keycloak); } @@ -20,11 +22,12 @@ export class IqserAuthGuard extends KeycloakAuthGuard { async isAccessAllowed(route: ActivatedRouteSnapshot): Promise { if (!this.authenticated) { const kcIdpHint = route.queryParamMap.get('kc_idp_hint'); - const options: any = { + const options: KeycloakLoginOptions = { redirectUri: window.location.href, }; - if (kcIdpHint ?? this._configService.values.OAUTH_IDP_HINT) { - options.idpHint = kcIdpHint ?? this._configService.values.OAUTH_IDP_HINT; + + if (kcIdpHint ?? this.#config.OAUTH_IDP_HINT) { + options.idpHint = kcIdpHint ?? this.#config.OAUTH_IDP_HINT; } await this._keycloak.login(options); return false; @@ -32,7 +35,7 @@ export class IqserAuthGuard extends KeycloakAuthGuard { const user = await this._userService.loadCurrentUser(); if (user?.hasAnyRole && route.routeConfig?.path === 'auth-error') { - await this._router.navigate([`/${this._tenantContextHolder.currentTenant}/main`]); + await this._router.navigate([`/${this._tenantsService.currentTenant}/main`]); return false; } diff --git a/src/lib/users/guards/iqser-role-guard.service.ts b/src/lib/users/guards/iqser-role-guard.service.ts index 1dfcdcb..810b630 100644 --- a/src/lib/users/guards/iqser-role-guard.service.ts +++ b/src/lib/users/guards/iqser-role-guard.service.ts @@ -2,19 +2,19 @@ import { inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; import { LoadingService } from '../../loading'; import { IqserUserService } from '../services/iqser-user.service'; -import { TenantContextHolder } from '../../tenants'; +import { TenantsService } from '../../tenants'; @Injectable() export class IqserRoleGuard implements CanActivate { protected readonly _router = inject(Router); - protected readonly _tenantContextHolder = inject(TenantContextHolder); + protected readonly _tenantsService = inject(TenantsService); protected readonly _loadingService = inject(LoadingService); protected readonly _userService = inject(IqserUserService); async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const currentUser = this._userService.currentUser; if (!currentUser || !currentUser.hasAnyRole) { - await this._router.navigate([`/${this._tenantContextHolder.currentTenant}/auth-error`]); + await this._router.navigate([`/${this._tenantsService.currentTenant}/auth-error`]); this._loadingService.stop(); return false; }