From d2fb511415a4b5d605f8ae2098cbf2cb54a019e6 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Wed, 6 Sep 2023 13:28:12 +0300 Subject: [PATCH 1/7] add tenant not found token --- .../abp/multi-tenancy/abp-tenant.service.ts | 3 ++- .../lib/tokens/tenant-not-found-by-name.ts | 3 +++ .../core/src/lib/utils/multi-tenancy-utils.ts | 20 +++++++++++++++---- .../oauth/src/lib/services/oauth.service.ts | 6 +++++- 4 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts diff --git a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts index 810f9a0dfe..63be5736d0 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { RestService } from '../../../../services/rest.service'; import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/multi-tenancy/models'; +import { tap } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -24,7 +25,7 @@ export class AbpTenantService { url: `/api/abp/multi-tenancy/tenants/by-name/${name}`, }, { apiName: this.apiName }, - ); + ).pipe(tap((res) => console.log(res))); constructor(private restService: RestService) {} } diff --git a/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts b/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts new file mode 100644 index 0000000000..febfaf4e57 --- /dev/null +++ b/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts @@ -0,0 +1,3 @@ +import { InjectionToken } from "@angular/core"; + +export const TENANT_NOT_FOUND_BY_NAME = new InjectionToken('TENANT_NOT_FOUND_BY_NAME'); diff --git a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts index a0b5aa89c6..338bf72e77 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts @@ -8,6 +8,7 @@ import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-te import { EnvironmentService } from '../services/environment.service'; import { MultiTenancyService } from '../services/multi-tenancy.service'; import { createTokenParser } from './string-utils'; +import { firstValueFrom } from 'rxjs'; const tenancyPlaceholder = '{0}'; @@ -24,6 +25,7 @@ function getCurrentTenancyNameFromUrl(tenantKey: string) { return urlParams.get(tenantKey); } + export async function parseTenantFromUrl(injector: Injector) { const environmentService = injector.get(EnvironmentService); const multiTenancyService = injector.get(MultiTenancyService); @@ -49,16 +51,26 @@ export async function parseTenantFromUrl(injector: Injector) { }; if (tenancyName) { + debugger /** * We have to replace tenant name within the urls from environment, * because the code below will make a http request to find information about the domain tenant. * Before this request takes place, we need to replace placeholders aka "{0}". */ replaceTenantNameWithinEnvironment(injector, tenancyName); - return multiTenancyService - .setTenantByName(tenancyName) - .pipe(tap(setEnvironmentWithDomainTenant)) - .toPromise(); + + const tenant$ = multiTenancyService + .setTenantByName(tenancyName) + .pipe(tap(setEnvironmentWithDomainTenant)) + try { + const result = await firstValueFrom(tenant$) + return result; + } + catch (error) { + + console.log(error) + debugger + } } else { /** * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests. diff --git a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts index e929ab7c79..8b10d39bf2 100644 --- a/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts +++ b/npm/ng-packs/packages/oauth/src/lib/services/oauth.service.ts @@ -1,6 +1,6 @@ import { Injectable, Injector } from '@angular/core'; import { Params } from '@angular/router'; -import { from, Observable, lastValueFrom } from 'rxjs'; +import { from, Observable, lastValueFrom, EMPTY } from 'rxjs'; import { filter, map, switchMap, take, tap } from 'rxjs/operators'; import { AbpAuthResponse, IAuthService, LoginParams } from '@abp/ng.core'; import { AuthFlowStrategy } from '../strategies'; @@ -44,6 +44,10 @@ export class AbpOAuthService implements IAuthService { } logout(queryParams?: Params): Observable { + + if(!this.strategy){ + return EMPTY + } return this.strategy.logout(queryParams); } From 0df76b87f3b3b1e6470f64437de43aa27583eb17 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Fri, 29 Sep 2023 16:25:57 +0300 Subject: [PATCH 2/7] Add Tenant-not found handler by token --- .../packages/core/src/lib/tokens/index.ts | 3 +- .../lib/tokens/tenant-not-found-by-name.ts | 7 +- .../core/src/lib/utils/multi-tenancy-utils.ts | 64 +++++++++++-------- .../theme-shared/src/lib/providers/index.ts | 1 + .../providers/tenant-not-found.provider.ts | 20 ++++++ .../src/lib/theme-shared.module.ts | 9 +-- 6 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 npm/ng-packs/packages/theme-shared/src/lib/providers/tenant-not-found.provider.ts diff --git a/npm/ng-packs/packages/core/src/lib/tokens/index.ts b/npm/ng-packs/packages/core/src/lib/tokens/index.ts index d01f652b98..12f4aae15a 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/index.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/index.ts @@ -12,4 +12,5 @@ export * from './pipe-to-login.token'; export * from './set-token-response-to-storage.token'; export * from './check-authentication-state'; export * from './http-context.token'; -export * from './others-group.token' \ No newline at end of file +export * from './others-group.token'; +export * from './tenant-not-found-by-name'; diff --git a/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts b/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts index febfaf4e57..639c1a3d1e 100644 --- a/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts +++ b/npm/ng-packs/packages/core/src/lib/tokens/tenant-not-found-by-name.ts @@ -1,3 +1,6 @@ -import { InjectionToken } from "@angular/core"; +import { InjectionToken } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; -export const TENANT_NOT_FOUND_BY_NAME = new InjectionToken('TENANT_NOT_FOUND_BY_NAME'); +export const TENANT_NOT_FOUND_BY_NAME = new InjectionToken< + (HttpErrorResponse: HttpErrorResponse) => void +>('TENANT_NOT_FOUND_BY_NAME'); diff --git a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts index 338bf72e77..cd1b9fe50e 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/multi-tenancy-utils.ts @@ -1,7 +1,5 @@ import { Injector } from '@angular/core'; import clone from 'just-clone'; - -import { tap } from 'rxjs/operators'; import { Environment } from '../models/environment'; import { FindTenantResultDto } from '../proxy/volo/abp/asp-net-core/mvc/multi-tenancy/models'; @@ -9,6 +7,8 @@ import { EnvironmentService } from '../services/environment.service'; import { MultiTenancyService } from '../services/multi-tenancy.service'; import { createTokenParser } from './string-utils'; import { firstValueFrom } from 'rxjs'; +import { TENANT_NOT_FOUND_BY_NAME } from '../tokens/tenant-not-found-by-name'; +import { HttpErrorResponse } from '@angular/common/http'; const tenancyPlaceholder = '{0}'; @@ -25,10 +25,10 @@ function getCurrentTenancyNameFromUrl(tenantKey: string) { return urlParams.get(tenantKey); } - export async function parseTenantFromUrl(injector: Injector) { const environmentService = injector.get(EnvironmentService); const multiTenancyService = injector.get(MultiTenancyService); + const tenantNotFoundHandler = injector.get(TENANT_NOT_FOUND_BY_NAME, null); const baseUrl = environmentService.getEnvironment()?.application?.baseUrl || ''; const tenancyName = getCurrentTenancyName(baseUrl); @@ -51,37 +51,39 @@ export async function parseTenantFromUrl(injector: Injector) { }; if (tenancyName) { - debugger /** * We have to replace tenant name within the urls from environment, * because the code below will make a http request to find information about the domain tenant. * Before this request takes place, we need to replace placeholders aka "{0}". */ replaceTenantNameWithinEnvironment(injector, tenancyName); - - const tenant$ = multiTenancyService - .setTenantByName(tenancyName) - .pipe(tap(setEnvironmentWithDomainTenant)) - try { - const result = await firstValueFrom(tenant$) - return result; - } - catch (error) { - - console.log(error) - debugger - } - } else { - /** - * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests. - */ - replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.'); - const tenantIdFromQueryParams = getCurrentTenancyNameFromUrl(multiTenancyService.tenantKey); - if (tenantIdFromQueryParams) { - return multiTenancyService.setTenantById(tenantIdFromQueryParams).toPromise(); + const tenant$ = multiTenancyService.setTenantByName(tenancyName); + try { + const result = await firstValueFrom(tenant$); + setEnvironmentWithDomainTenant(result); + return Promise.resolve(result); + } catch (httpError: HttpErrorResponse | any) { + if ( + httpError instanceof HttpErrorResponse && + httpError.status === 404 && + tenantNotFoundHandler + ) { + tenantNotFoundHandler(httpError); + } + return Promise.reject(); } } + /** + * If there is no tenant, we still have to clean up {0}. from baseUrl to avoid incorrect http requests. + */ + replaceTenantNameWithinEnvironment(injector, '', tenancyPlaceholder + '.'); + + const tenantIdFromQueryParams = getCurrentTenancyNameFromUrl(multiTenancyService.tenantKey); + if (tenantIdFromQueryParams) { + const tenantById$ = multiTenancyService.setTenantById(tenantIdFromQueryParams); + return firstValueFrom(tenantById$); + } return Promise.resolve(); } @@ -109,14 +111,20 @@ function replaceTenantNameWithinEnvironment( ); } - if(!environment.oAuthConfig) { + if (!environment.oAuthConfig) { environment.oAuthConfig = {}; } - environment.oAuthConfig.issuer = (environment.oAuthConfig.issuer || '').replace(placeholder, tenancyName); + environment.oAuthConfig.issuer = (environment.oAuthConfig.issuer || '').replace( + placeholder, + tenancyName, + ); Object.keys(environment.apis).forEach(api => { Object.keys(environment.apis[api]).forEach(key => { - environment.apis[api][key] = (environment.apis[api][key] || '').replace(placeholder, tenancyName); + environment.apis[api][key] = (environment.apis[api][key] || '').replace( + placeholder, + tenancyName, + ); }); }); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts index 40efb9df39..076d6169ba 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/providers/index.ts @@ -1,2 +1,3 @@ export * from './ng-bootstrap-config.provider'; export * from './route.provider'; +export * from './tenant-not-found.provider'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/providers/tenant-not-found.provider.ts b/npm/ng-packs/packages/theme-shared/src/lib/providers/tenant-not-found.provider.ts new file mode 100644 index 0000000000..ac98f613b4 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/providers/tenant-not-found.provider.ts @@ -0,0 +1,20 @@ +import { TENANT_NOT_FOUND_BY_NAME } from '@abp/ng.core'; +import { inject, Provider } from '@angular/core'; +import { ConfirmationService } from '../services'; +import { HttpErrorResponse } from '@angular/common/http'; + +export const tenantNotFoundProvider: Provider = { + provide: TENANT_NOT_FOUND_BY_NAME, + useFactory: function () { + const confirm = inject(ConfirmationService); + return (response: HttpErrorResponse) => { + const { error } = response.error; + // hide loading donut + const appRoot = document.querySelector('app-root div.donut'); + if (appRoot) { + appRoot.remove(); + } + confirm.error(error.details, error.message, { hideCancelBtn: true, hideYesBtn: true }); + }; + }, +}; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 0fea3f166c..030418eadf 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -40,6 +40,7 @@ import { CardModule } from './components/card/card.module'; import { AbpVisibleDirective } from './directives'; import { FormInputComponent } from './components/form-input/form-input.component'; import { FormCheckboxComponent } from './components/checkbox/checkbox.component'; +import { tenantNotFoundProvider } from './providers/tenant-not-found.provider'; const declarationsWithExports = [ BreadcrumbComponent, @@ -58,7 +59,7 @@ const declarationsWithExports = [ ModalCloseDirective, AbpVisibleDirective, FormInputComponent, - FormCheckboxComponent + FormCheckboxComponent, ]; @NgModule({ @@ -69,7 +70,6 @@ const declarationsWithExports = [ NgbPaginationModule, EllipsisModule, CardModule, - ], declarations: [...declarationsWithExports, HttpErrorWrapperComponent], exports: [ @@ -77,11 +77,11 @@ const declarationsWithExports = [ EllipsisModule, NgxValidateCoreModule, CardModule, - ...declarationsWithExports + ...declarationsWithExports, ], providers: [DatePipe], }) -export class BaseThemeSharedModule { } +export class BaseThemeSharedModule {} @NgModule({ imports: [BaseThemeSharedModule], @@ -144,6 +144,7 @@ export class ThemeSharedModule { ...(confirmationIcons || {}), }, }, + tenantNotFoundProvider, ], }; } From aa2c6f089c964c4ff13a461d0ebc006377995203 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Fri, 29 Sep 2023 16:40:33 +0300 Subject: [PATCH 3/7] Add docs for how to customize tenant not found --- docs/en/UI/Angular/Multi-Tenancy.md | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md index 92f76435ca..f133e5f25e 100644 --- a/docs/en/UI/Angular/Multi-Tenancy.md +++ b/docs/en/UI/Angular/Multi-Tenancy.md @@ -125,6 +125,37 @@ After this replacement, the app will use the following URLs: The app sends the `__tenant` header that contains the current tenant id on each request. +### Handling Domain-based Tenant not found + +When the application is initiated and a domain-based tenant is active, it makes a request for validating tenant. If it cannot be located. The Angular application calls the function. The function displays an error message, but you can customize the functionality by injecting the 'TENANT_NOT_FOUND_BY_NAME' token into the function. + +Example: +```ts +import { TENANT_NOT_FOUND_BY_NAME } from '@abp/ng.core'; + +@NgModule({ + imports: [ + // removed for clarity + ], + providers: [ + // removed for clarity + { + provide: TENANT_NOT_FOUND_BY_NAME, + useFactory: function () { + return () => { + window.location.replace('https://abp.io'); + }; + }, + }, + ], + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} + +``` + ## See Also - [Multi Tenancy in ABP](../../Multi-Tenancy.md) + From 660ae8504a8afac1308d2fd1785422fc7087fee0 Mon Sep 17 00:00:00 2001 From: Masum ULU <49063256+masumulu28@users.noreply.github.com> Date: Sun, 1 Oct 2023 18:01:07 +0300 Subject: [PATCH 4/7] Quick doc fix --- docs/en/UI/Angular/Multi-Tenancy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md index f133e5f25e..f897519ec0 100644 --- a/docs/en/UI/Angular/Multi-Tenancy.md +++ b/docs/en/UI/Angular/Multi-Tenancy.md @@ -127,7 +127,7 @@ The app sends the `__tenant` header that contains the current tenant id on each ### Handling Domain-based Tenant not found -When the application is initiated and a domain-based tenant is active, it makes a request for validating tenant. If it cannot be located. The Angular application calls the function. The function displays an error message, but you can customize the functionality by injecting the 'TENANT_NOT_FOUND_BY_NAME' token into the function. +When the application is initialized and a domain-based tenant is active, it makes a request for validating tenant. If it cannot be located, the Angular application calls the function. The function displays an error message, but you can customize the functionality by injecting the `TENANT_NOT_FOUND_BY_NAME` token into the function. Example: ```ts From 315a1afdbbb41d14b8a0eb7570f99a14348691c1 Mon Sep 17 00:00:00 2001 From: Hamza Albreem <94292623+braim23@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:20:40 +0300 Subject: [PATCH 5/7] Update Multi-Tenancy.md --- docs/en/UI/Angular/Multi-Tenancy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/UI/Angular/Multi-Tenancy.md b/docs/en/UI/Angular/Multi-Tenancy.md index f897519ec0..493f6bf194 100644 --- a/docs/en/UI/Angular/Multi-Tenancy.md +++ b/docs/en/UI/Angular/Multi-Tenancy.md @@ -127,7 +127,7 @@ The app sends the `__tenant` header that contains the current tenant id on each ### Handling Domain-based Tenant not found -When the application is initialized and a domain-based tenant is active, it makes a request for validating tenant. If it cannot be located, the Angular application calls the function. The function displays an error message, but you can customize the functionality by injecting the `TENANT_NOT_FOUND_BY_NAME` token into the function. +When the application is initialized and a domain-based tenant is active, it makes a request for to validate the tenant. If it cannot be located, the Angular application calls the function. The function displays an error message, but the functionality can be customized by injecting the `TENANT_NOT_FOUND_BY_NAME` token into the function. Example: ```ts From 19f0888cee13c17e936a367a0df7f13e80e2f4db Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Mon, 2 Oct 2023 09:45:51 +0300 Subject: [PATCH 6/7] remove debug codes --- .../src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts index 63be5736d0..7e55a429fe 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts @@ -25,7 +25,7 @@ export class AbpTenantService { url: `/api/abp/multi-tenancy/tenants/by-name/${name}`, }, { apiName: this.apiName }, - ).pipe(tap((res) => console.log(res))); + ); constructor(private restService: RestService) {} } From 3b20723cc68add4558fc5a65383bea8cb609d575 Mon Sep 17 00:00:00 2001 From: Mahmut Gundogdu Date: Mon, 2 Oct 2023 09:46:46 +0300 Subject: [PATCH 7/7] remove unused import --- .../src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts index 7e55a429fe..810f9a0dfe 100644 --- a/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts +++ b/npm/ng-packs/packages/core/src/lib/proxy/pages/abp/multi-tenancy/abp-tenant.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { RestService } from '../../../../services/rest.service'; import type { FindTenantResultDto } from '../../../volo/abp/asp-net-core/mvc/multi-tenancy/models'; -import { tap } from 'rxjs'; @Injectable({ providedIn: 'root',