Skip to content

Commit 6aca74e

Browse files
committed
Merge branch 'master' into System.Text.Json
2 parents d99844f + da96b3d commit 6aca74e

File tree

18 files changed

+261
-462
lines changed

18 files changed

+261
-462
lines changed

angular/src/app-initializer.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { filter as _filter, merge as _merge } from 'lodash-es';
66
import { AppConsts } from '@shared/AppConsts';
77
import { AppSessionService } from '@shared/session/app-session.service';
88
import { environment } from './environments/environment';
9+
import { AccountServiceProxy, IsTenantAvailableInput, IsTenantAvailableOutput, TenantAvailabilityState } from '@shared/service-proxies/service-proxies';
10+
import { SubdomainTenantResolver } from '@shared/multi-tenancy/tenant-resolvers/subdomain-tenant-resolver';
911

1012
@Injectable({
1113
providedIn: 'root',
@@ -164,7 +166,40 @@ export class AppInitializer {
164166
AppConsts.remoteServiceBaseUrl = response.remoteServiceBaseUrl;
165167
AppConsts.localeMappings = response.localeMappings;
166168

167-
callback();
169+
// Find tenant from subdomain
170+
var tenancyName = this.resolveTenancyName(response.appBaseUrl);
171+
172+
if (tenancyName == null) {
173+
callback();
174+
} else {
175+
this.ConfigureTenantIdCookie(tenancyName, callback);
176+
}
168177
});
169178
}
179+
180+
private ConfigureTenantIdCookie(tenancyName: string, callback: () => void) {
181+
let accountServiceProxy: AccountServiceProxy = this._injector.get(AccountServiceProxy);
182+
let input = new IsTenantAvailableInput();
183+
input.tenancyName = tenancyName;
184+
185+
accountServiceProxy.isTenantAvailable(input).subscribe((result: IsTenantAvailableOutput) => {
186+
if (result.state === TenantAvailabilityState._1) { // Available
187+
abp.multiTenancy.setTenantIdCookie(result.tenantId);
188+
}
189+
190+
callback();
191+
});
192+
}
193+
194+
private resolveTenancyName(appBaseUrl): string | null {
195+
var subdomainTenantResolver = new SubdomainTenantResolver();
196+
var tenancyName = subdomainTenantResolver.resolve(appBaseUrl);
197+
if (tenancyName) {
198+
return tenancyName;
199+
}
200+
201+
// add other tenancy resolvers here, ex: CookieTenantResolver, QueryStringTenantResolver etc...
202+
203+
return null;
204+
}
170205
}

angular/src/shared/AppConsts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export class AppConsts {
22

3+
static readonly tenancyNamePlaceHolderInUrl = '{TENANCY_NAME}';
4+
35
static remoteServiceBaseUrl: string;
46
static appBaseUrl: string;
57
static appBaseHref: string; // returns angular's base-href parameter value if used during the publish
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
class ExtractionResult {
2+
public IsMatch: boolean;
3+
public Matches: any[];
4+
5+
constructor(isMatch: boolean) {
6+
this.IsMatch = isMatch;
7+
this.Matches = [];
8+
}
9+
}
10+
11+
enum FormatStringTokenType {
12+
ConstantText,
13+
DynamicValue,
14+
}
15+
16+
class FormatStringToken {
17+
public Text: string;
18+
19+
public Type: FormatStringTokenType;
20+
21+
constructor(text: string, type: FormatStringTokenType) {
22+
this.Text = text;
23+
this.Type = type;
24+
}
25+
}
26+
27+
class FormatStringTokenizer {
28+
Tokenize(format: string, includeBracketsForDynamicValues: boolean = false): FormatStringToken[] {
29+
const tokens: FormatStringToken[] = [];
30+
31+
let currentText = '';
32+
let inDynamicValue = false;
33+
34+
for (let i = 0; i < format.length; i++) {
35+
const c = format[i];
36+
switch (c) {
37+
case '{':
38+
if (inDynamicValue) {
39+
throw new Error(
40+
'Incorrect syntax at char ' +
41+
i +
42+
'! format string can not contain nested dynamic value expression!'
43+
);
44+
}
45+
46+
inDynamicValue = true;
47+
48+
if (currentText.length > 0) {
49+
tokens.push(new FormatStringToken(currentText, FormatStringTokenType.ConstantText));
50+
currentText = '';
51+
}
52+
53+
break;
54+
case '}':
55+
if (!inDynamicValue) {
56+
throw new Error(
57+
'Incorrect syntax at char ' +
58+
i +
59+
'! These is no opening brackets for the closing bracket }.'
60+
);
61+
}
62+
63+
inDynamicValue = false;
64+
65+
if (currentText.length <= 0) {
66+
throw new Error('Incorrect syntax at char ' + i + '! Brackets does not containt any chars.');
67+
}
68+
69+
let dynamicValue = currentText;
70+
if (includeBracketsForDynamicValues) {
71+
dynamicValue = '{' + dynamicValue + '}';
72+
}
73+
74+
tokens.push(new FormatStringToken(dynamicValue, FormatStringTokenType.DynamicValue));
75+
currentText = '';
76+
77+
break;
78+
default:
79+
currentText += c;
80+
break;
81+
}
82+
}
83+
84+
if (inDynamicValue) {
85+
throw new Error('There is no closing } char for an opened { char.');
86+
}
87+
88+
if (currentText.length > 0) {
89+
tokens.push(new FormatStringToken(currentText, FormatStringTokenType.ConstantText));
90+
}
91+
92+
return tokens;
93+
}
94+
}
95+
96+
export class FormattedStringValueExtracter {
97+
Extract(str: string, format: string): ExtractionResult {
98+
if (str === format) {
99+
return new ExtractionResult(true);
100+
}
101+
102+
const formatTokens = new FormatStringTokenizer().Tokenize(format);
103+
if (!formatTokens) {
104+
return new ExtractionResult(str === '');
105+
}
106+
107+
const result = new ExtractionResult(true);
108+
109+
for (let i = 0; i < formatTokens.length; i++) {
110+
const currentToken = formatTokens[i];
111+
const previousToken = i > 0 ? formatTokens[i - 1] : null;
112+
113+
if (currentToken.Type === FormatStringTokenType.ConstantText) {
114+
if (i === 0) {
115+
if (str.indexOf(currentToken.Text) !== 0) {
116+
result.IsMatch = false;
117+
return result;
118+
}
119+
120+
str = str.substr(currentToken.Text.length, str.length - currentToken.Text.length);
121+
} else {
122+
const matchIndex = str.indexOf(currentToken.Text);
123+
if (matchIndex < 0) {
124+
result.IsMatch = false;
125+
return result;
126+
}
127+
128+
result.Matches.push({ name: previousToken?.Text, value: str.substr(0, matchIndex) });
129+
str = str.substring(0, matchIndex + currentToken.Text.length);
130+
}
131+
}
132+
}
133+
134+
const lastToken = formatTokens[formatTokens.length - 1];
135+
if (lastToken.Type === FormatStringTokenType.DynamicValue) {
136+
result.Matches.push({ name: lastToken.Text, value: str });
137+
}
138+
139+
return result;
140+
}
141+
142+
IsMatch(str: string, format: string): string[] {
143+
const result = new FormattedStringValueExtracter().Extract(str, format);
144+
if (!result.IsMatch) {
145+
return [];
146+
}
147+
148+
const values: any[] = [];
149+
for (let i = 0; i < result.Matches.length; i++) {
150+
values.push(result.Matches[i].value);
151+
}
152+
153+
return values;
154+
}
155+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { AppConsts } from '@shared/AppConsts';
2+
import { FormattedStringValueExtracter } from '@shared/helpers/FormattedStringValueExtracter';
3+
4+
export class SubdomainTenancyNameFinder {
5+
urlHasTenancyNamePlaceholder(url: string): boolean {
6+
return url.indexOf(AppConsts.tenancyNamePlaceHolderInUrl) >= 0;
7+
}
8+
9+
getCurrentTenancyNameOrNull(rootAddress: string): string | null {
10+
if (rootAddress.indexOf(AppConsts.tenancyNamePlaceHolderInUrl) < 0) {
11+
// Web site does not support subdomain tenant name
12+
return null;
13+
}
14+
15+
const currentRootAddress = document.location.href;
16+
17+
const formattedStringValueExtracter = new FormattedStringValueExtracter();
18+
const values: any[] = formattedStringValueExtracter.IsMatch(currentRootAddress, rootAddress);
19+
if (!values.length) {
20+
return null;
21+
}
22+
23+
return values[0];
24+
}
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SubdomainTenancyNameFinder } from '@shared/helpers/SubdomainTenancyNameFinder';
2+
3+
export class SubdomainTenantResolver {
4+
5+
resolve(appBaseUrl): string {
6+
const subdomainTenancyNameFinder = new SubdomainTenancyNameFinder();
7+
return subdomainTenancyNameFinder.getCurrentTenancyNameOrNull(appBaseUrl);
8+
}
9+
10+
}

0 commit comments

Comments
 (0)