mirror of https://github.com/abpframework/abp.git
8 changed files with 254 additions and 3 deletions
@ -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]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue