From d1c0559099be64cb608b87afde6b4ee57de2604f Mon Sep 17 00:00:00 2001 From: George Date: Wed, 29 Nov 2023 17:06:17 +0200 Subject: [PATCH] RED-3800, refactor multitenancy --- src/lib/listing/listing.module.ts | 2 -- .../table-content.component.html | 4 +-- src/lib/services/api-path.interceptor.ts | 6 ++-- .../services/iqser-user-preference.service.ts | 7 +++-- src/lib/tenants/index.ts | 1 - src/lib/tenants/keycloak-initializer.ts | 5 ++-- .../services/keycloak-status.service.ts | 17 +++++------ .../tenant-select/tenant-select.component.ts | 6 ++-- src/lib/tenants/tenant.pipe.ts | 19 ------------ src/lib/users/services/iqser-user.service.ts | 9 +++--- src/lib/utils/tokens.ts | 29 ++++--------------- 11 files changed, 32 insertions(+), 73 deletions(-) delete mode 100644 src/lib/tenants/tenant.pipe.ts diff --git a/src/lib/listing/listing.module.ts b/src/lib/listing/listing.module.ts index 575ac93..bd16572 100644 --- a/src/lib/listing/listing.module.ts +++ b/src/lib/listing/listing.module.ts @@ -21,7 +21,6 @@ import { CircleButtonComponent, IconButtonComponent } from '../buttons'; import { MatIconModule } from '@angular/material/icon'; import { EmptyStateComponent } from '../empty-state'; import { InputWithActionComponent, RoundCheckboxComponent } from '../inputs'; -import { TenantPipe } from '../tenants/tenant.pipe'; const matModules = [MatTooltipModule, MatIconModule]; const components = [ @@ -51,7 +50,6 @@ const modules = [DragDropModule, TranslateModule, IqserFiltersModule, ScrollingM RoundCheckboxComponent, InputWithActionComponent, SyncWidthDirective, - TenantPipe, ], }) export class IqserListingModule {} diff --git a/src/lib/listing/table-content/table-content.component.html b/src/lib/listing/table-content/table-content.component.html index ea8775c..b086b4d 100644 --- a/src/lib/listing/table-content/table-content.component.html +++ b/src/lib/listing/table-content/table-content.component.html @@ -17,7 +17,7 @@ [class.help-mode-active]="helpModeService?.isHelpModeActive$ | async" [id]="'item-' + entity.id" [ngClass]="getTableItemClasses(entity)" - [routerLink]="entity.routerLink | tenant" + [routerLink]="entity.routerLink" > , next: HttpHandler): Observable> { if (!req.url.startsWith('/assets')) { @@ -15,7 +15,7 @@ export class ApiPathInterceptor implements HttpInterceptor { return next.handle(req.clone({ url: apiUrl })); } - const url = this.#baseHref + req.url; + const url = this.#convertPath(req.url); return next.handle(req.clone({ url })); } } diff --git a/src/lib/services/iqser-user-preference.service.ts b/src/lib/services/iqser-user-preference.service.ts index 104b4cf..8aa443a 100644 --- a/src/lib/services/iqser-user-preference.service.ts +++ b/src/lib/services/iqser-user-preference.service.ts @@ -1,7 +1,8 @@ import { inject, Injectable } from '@angular/core'; import { firstValueFrom } from 'rxjs'; -import { BASE_HREF, List } from '../utils'; +import { List } from '../utils'; import { GenericService } from './generic.service'; +import { APP_BASE_HREF } from '@angular/common'; export type UserAttributes = Record; @@ -12,9 +13,9 @@ const KEYS = { @Injectable() export abstract class IqserUserPreferenceService extends GenericService { - #userAttributes: UserAttributes = {}; protected abstract readonly _devFeaturesEnabledKey: string; protected readonly _serviceName: string = 'tenant-user-management'; + #userAttributes: UserAttributes = {}; get userAttributes(): UserAttributes { return this.#userAttributes; @@ -72,7 +73,7 @@ export abstract class IqserUserPreferenceService extends GenericService 0) { - return window.location.href; + return window.location.href + '/main'; } - const url = window.location.origin + this.#baseHref; + const origin = window.location.origin; if (tenant) { - return url + '/' + tenant; + return origin + '/ui/' + tenant + '/main'; } - return url; + return origin + '/ui'; } } diff --git a/src/lib/tenants/tenant-select/tenant-select.component.ts b/src/lib/tenants/tenant-select/tenant-select.component.ts index 4909b81..5133617 100644 --- a/src/lib/tenants/tenant-select/tenant-select.component.ts +++ b/src/lib/tenants/tenant-select/tenant-select.component.ts @@ -5,10 +5,10 @@ import { KeycloakService } from 'keycloak-angular'; import { NGXLogger } from 'ngx-logger'; import { LoadingService } from '../../loading'; import { getConfig } from '../../services'; -import { BASE_HREF } from '../../utils'; import { getKeycloakOptions } from '../keycloak-initializer'; import { IStoredTenantId, TenantsService } from '../services'; import { KeycloakStatusService } from '../services/keycloak-status.service'; +import { APP_BASE_HREF } from '@angular/common'; @Component({ templateUrl: './tenant-select.component.html', @@ -16,7 +16,8 @@ import { KeycloakStatusService } from '../services/keycloak-status.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class TenantSelectComponent { - protected readonly baseHref = inject(BASE_HREF); + @Input() isLoggedOut = false; + protected readonly baseHref = inject(APP_BASE_HREF); protected readonly logger = inject(NGXLogger); protected readonly tenantsService = inject(TenantsService); protected storedTenants: IStoredTenantId[] = []; @@ -29,7 +30,6 @@ export class TenantSelectComponent { // eslint-disable-next-line @typescript-eslint/unbound-method tenantId: ['', Validators.required], }); - @Input() isLoggedOut = false; constructor() { this.#loadStoredTenants(); diff --git a/src/lib/tenants/tenant.pipe.ts b/src/lib/tenants/tenant.pipe.ts deleted file mode 100644 index 8b581b7..0000000 --- a/src/lib/tenants/tenant.pipe.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { inject, Pipe, PipeTransform } from '@angular/core'; -import { TenantsService } from './services'; - -@Pipe({ - name: 'tenant', - pure: true, - standalone: true, -}) -export class TenantPipe implements PipeTransform { - readonly #tenantsService = inject(TenantsService); - - transform(value: string | string[]): string | undefined { - if (!value) { - return undefined; - } - const _value = Array.isArray(value) ? value.join('/') : value; - return '/' + this.#tenantsService.activeTenantId + _value; - } -} diff --git a/src/lib/users/services/iqser-user.service.ts b/src/lib/users/services/iqser-user.service.ts index 76800f1..2e598fc 100644 --- a/src/lib/users/services/iqser-user.service.ts +++ b/src/lib/users/services/iqser-user.service.ts @@ -5,13 +5,12 @@ import { KeycloakService } from 'keycloak-angular'; import { KeycloakProfile } from 'keycloak-js'; import { BehaviorSubject, firstValueFrom, Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; -import { IProfile } from '../../../../../red-domain/src'; import { CacheApiService } from '../../caching'; import { EntitiesService } from '../../listing'; import { IqserPermissionsService, IqserRolesService } from '../../permissions'; import { QueryParam, Toaster } from '../../services'; import { KeycloakStatusService } from '../../tenants'; -import { BASE_HREF, List, mapEach } from '../../utils'; +import { List, mapEach, UI_ROOT } from '../../utils'; import { IqserUser } from '../iqser-user.model'; import { ICreateUserRequest } from '../types/create-user.request'; import { IMyProfileUpdateRequest } from '../types/my-profile-update.request'; @@ -24,6 +23,7 @@ export abstract class IqserUserService< Interface extends IIqserUser = IIqserUser, Class extends IqserUser & Interface = IqserUser & Interface, > extends EntitiesService { + readonly currentUser$: Observable; protected abstract readonly _defaultModelPath: string; protected abstract readonly _permissionsFilter: (role: string) => boolean; protected abstract readonly _rolesFilter: (role: string) => boolean; @@ -35,9 +35,8 @@ export abstract class IqserUserService< protected readonly _keycloakStatusService = inject(KeycloakStatusService); protected readonly _permissionsService = inject(IqserPermissionsService, { optional: true }); protected readonly _rolesService = inject(IqserRolesService, { optional: true }); - protected readonly _baseHref = inject(BASE_HREF); protected readonly _serviceName: string = 'tenant-user-management'; - readonly currentUser$: Observable; + readonly #uiRoot = inject(UI_ROOT); constructor() { super(); @@ -63,7 +62,7 @@ export abstract class IqserUserService< try { await this._keycloakService.loadUserProfile(true); await this._cacheApiService.wipeCaches(); - const redirectUri = window.location.origin + this._baseHref + '/?isLoggedOut=true'; + const redirectUri = window.location.origin + this.#uiRoot + '/?isLoggedOut=true'; await this._keycloakService.logout(redirectUri); } catch (e) { console.log('Logout failed: ', e); diff --git a/src/lib/utils/tokens.ts b/src/lib/utils/tokens.ts index a7cef86..cf59db3 100644 --- a/src/lib/utils/tokens.ts +++ b/src/lib/utils/tokens.ts @@ -1,35 +1,16 @@ import { inject, InjectionToken } from '@angular/core'; -import { PlatformLocation } from '@angular/common'; -export const BASE_HREF = new InjectionToken('BASE_HREF', { +export const UI_ROOT = new InjectionToken('UI path root - different from BASE_HREF'); +export const UI_ROOT_PATH_FN = new InjectionToken<(path: string) => string>('Append UI root to path', { factory: () => { - const baseUrl = inject(PlatformLocation).getBaseHrefFromDOM(); - if (!baseUrl) { - return ''; - } - - if (baseUrl[baseUrl.length - 1] === '/') { - return baseUrl.substring(0, baseUrl.length - 1); - } - - console.log('Base URL:', baseUrl); - - return baseUrl; - }, -}); - -export type BaseHrefFn = (path: string) => string; - -export const BASE_HREF_FN = new InjectionToken('Convert path function', { - factory: () => { - const baseUrl = inject(BASE_HREF); + const root = inject(UI_ROOT); return (path: string) => { if (path[0] === '/') { - return baseUrl + path; + return root + path; } - return baseUrl + '/' + path; + return root + '/' + path; }; }, });