feat: add multi-tenancy-utils file

pull/4956/head
mehmet-erim 5 years ago
parent 70308947b6
commit 57cd025ba5

@ -34,10 +34,10 @@ import { ConfigState } from './states/config.state';
import { ProfileState } from './states/profile.state';
import { ReplaceableComponentsState } from './states/replaceable-components.state';
import { SessionState } from './states/session.state';
import { CORE_OPTIONS, coreOptionsFactory } from './tokens/options.token';
import { coreOptionsFactory, CORE_OPTIONS } from './tokens/options.token';
import { noop } from './utils/common-utils';
import './utils/date-extensions';
import { getInitialData, localeInitializer, configureOAuth } from './utils/initial-utils';
import { configureOAuth, getInitialData, localeInitializer } from './utils/initial-utils';
export function storageFactory(): OAuthStorage {
return localStorage;

@ -17,6 +17,7 @@ export namespace Config {
export interface Application {
name: string;
baseUrl?: string;
logoUrl?: string;
}

@ -0,0 +1,16 @@
export class FindTenantResultDto {
success: boolean;
tenantId?: string;
name: string;
constructor(initialValues: Partial<FindTenantResultDto> = {}) {
if (initialValues) {
for (const key in initialValues) {
if (initialValues.hasOwnProperty(key)) {
this[key] = initialValues[key];
}
}
}
}
}

@ -2,6 +2,7 @@ export * from './application-configuration';
export * from './common';
export * from './config';
export * from './dtos';
export * from './find-tenant-result-dto';
export * from './profile';
export * from './replaceable-components';
export * from './rest';

@ -0,0 +1,156 @@
class ExtractionResult {
public isMatch: boolean;
public matches: any[];
constructor(isMatch: boolean) {
this.isMatch = isMatch;
this.matches = [];
}
}
enum FormatStringTokenType {
ConstantText,
DynamicValue,
}
class FormatStringToken {
public text: string;
public type: FormatStringTokenType;
constructor(text: string, type: FormatStringTokenType) {
this.text = text;
this.type = type;
}
}
class FormatStringTokenizer {
tokenize(format: string, includeBracketsForDynamicValues: boolean = false): FormatStringToken[] {
const tokens: FormatStringToken[] = [];
let currentText = '';
let inDynamicValue = false;
for (let i = 0; i < format.length; i++) {
const c = format[i];
switch (c) {
case '{':
if (inDynamicValue) {
throw new Error(
'Incorrect syntax at char ' +
i +
'! format string can not contain nested dynamic value expression!',
);
}
inDynamicValue = true;
if (currentText.length > 0) {
tokens.push(new FormatStringToken(currentText, FormatStringTokenType.ConstantText));
currentText = '';
}
break;
case '}':
if (!inDynamicValue) {
throw new Error(
'Incorrect syntax at char ' +
i +
'! These is no opening brackets for the closing bracket }.',
);
}
inDynamicValue = false;
if (currentText.length <= 0) {
throw new Error(
'Incorrect syntax at char ' + i + '! Brackets does not containt any chars.',
);
}
let dynamicValue = currentText;
if (includeBracketsForDynamicValues) {
dynamicValue = '{' + dynamicValue + '}';
}
tokens.push(new FormatStringToken(dynamicValue, FormatStringTokenType.DynamicValue));
currentText = '';
break;
default:
currentText += c;
break;
}
}
if (inDynamicValue) {
throw new Error('There is no closing } char for an opened { char.');
}
if (currentText.length > 0) {
tokens.push(new FormatStringToken(currentText, FormatStringTokenType.ConstantText));
}
return tokens;
}
}
export class FormattedStringValueExtractor {
extract(str: string, format: string): ExtractionResult {
if (str === format) {
return new ExtractionResult(true);
}
const formatTokens = new FormatStringTokenizer().tokenize(format);
if (!formatTokens) {
return new ExtractionResult(str === '');
}
const result = new ExtractionResult(true);
for (let i = 0; i < formatTokens.length; i++) {
const currentToken = formatTokens[i];
const previousToken = i > 0 ? formatTokens[i - 1] : null;
if (currentToken.type === FormatStringTokenType.ConstantText) {
if (i === 0) {
if (str.indexOf(currentToken.text) !== 0) {
result.isMatch = false;
return result;
}
str = str.substr(currentToken.text.length, str.length - currentToken.text.length);
} else {
const matchIndex = str.indexOf(currentToken.text);
if (matchIndex < 0) {
result.isMatch = false;
return result;
}
result.matches.push({ name: previousToken.text, value: str.substr(0, matchIndex) });
str = str.substring(0, matchIndex + currentToken.text.length);
}
}
}
const lastToken = formatTokens[formatTokens.length - 1];
if (lastToken.type === FormatStringTokenType.DynamicValue) {
result.matches.push({ name: lastToken.text, value: str });
}
return result;
}
isMatch(str: string, format: string): string[] {
const result = new FormattedStringValueExtractor().extract(str, format);
if (!result.isMatch) {
return [];
}
const values = [];
for (let i = 0; i < result.matches.length; i++) {
values.push(result.matches[i].value);
}
return values;
}
}

@ -9,4 +9,5 @@ export * from './localization-utils';
export * from './number-utils';
export * from './route-utils';
export * from './rxjs-utils';
export * from './multi-tenancy-utils';
export * from './tree-utils';

@ -7,6 +7,7 @@ import { GetAppConfiguration } from '../actions/config.actions';
import { ABP } from '../models/common';
import { ConfigState } from '../states/config.state';
import { CORE_OPTIONS } from '../tokens/options.token';
import { parseTenantFromUrl } from './multi-tenancy-utils';
export function configureOAuth(injector: Injector, options: ABP.Root) {
const fn = () => {
@ -19,10 +20,12 @@ export function configureOAuth(injector: Injector, options: ABP.Root) {
}
export function getInitialData(injector: Injector) {
const fn = () => {
const fn = async () => {
const store: Store = injector.get(Store);
const { skipGetAppConfiguration } = injector.get(CORE_OPTIONS) as ABP.Root;
await parseTenantFromUrl(injector);
if (skipGetAppConfiguration) return;
return store

@ -0,0 +1,73 @@
import { Injector } from '@angular/core';
import { Store } from '@ngxs/store';
import { ConfigState } from '../states/config.state';
import { Config } from '../models/config';
import { FormattedStringValueExtractor } from './formatted-string-value-extractor';
import { MultiTenancyService } from '../services/multi-tenancy.service';
import { tap, switchMap } from 'rxjs/operators';
import { SetTenant, SetEnvironment } from '../actions';
import { of } from 'rxjs';
const tenancyPlaceholder = '{TENANCY_NAME}';
export function getCurrentTenancyNameOrNull(appBaseUrl: string): string {
if (appBaseUrl.indexOf(tenancyPlaceholder) < 0) return null;
const currentRootAddress = document.location.href;
const formattedStringValueExtracter = new FormattedStringValueExtractor();
const values: any[] = formattedStringValueExtracter.isMatch(currentRootAddress, appBaseUrl);
if (!values.length) {
return null;
}
return values[0];
}
export async function parseTenantFromUrl(injector: Injector) {
const store: Store = injector.get(Store);
const multiTenancyService = injector.get(MultiTenancyService);
const environment = store.selectSnapshot(ConfigState.getOne('environment')) as Config.Environment;
const { baseUrl = '' } = environment.application;
const tenancyName = getCurrentTenancyNameOrNull(baseUrl);
if (tenancyName) {
multiTenancyService.isTenantBoxVisible = false;
return setEnvironment(store, tenancyName)
.pipe(
switchMap(() => multiTenancyService.findTenantByName(tenancyName, { __tenant: '' })),
tap(res => {
if (!res.success) return;
const tenant = { id: res.tenantId, name: res.name };
multiTenancyService.domainTenant = tenant;
}),
)
.toPromise();
}
return Promise.resolve();
}
export function setEnvironment(store: Store, tenancyName: string) {
const environment = store.selectSnapshot(ConfigState.getOne('environment')) as Config.Environment;
if (environment.application.baseUrl) {
environment.application.baseUrl = environment.application.baseUrl.replace(
tenancyPlaceholder,
tenancyName,
);
}
Object.keys(environment.apis).forEach(api => {
Object.keys(environment.apis[api]).forEach(key => {
environment.apis[api][key] = environment.apis[api][key].replace(
tenancyPlaceholder,
tenancyName,
);
});
});
return store.dispatch(new SetEnvironment(environment));
}
Loading…
Cancel
Save