@pwrdrvr/microapps-router-lib 0.4.0-alpha.8 → 0.4.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-cache.d.ts +58 -0
- package/dist/app-cache.d.ts.map +1 -0
- package/dist/app-cache.js +154 -0
- package/dist/app-cache.js.map +1 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +375 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/log.d.ts +5 -0
- package/dist/lib/log.d.ts.map +1 -0
- package/dist/lib/log.js +11 -0
- package/dist/lib/log.js.map +1 -0
- package/package.json +2 -1
- package/src/app-cache.spec.ts +175 -0
- package/src/app-cache.ts +193 -0
- package/src/index.spec.ts +28 -24
- package/src/index.ts +46 -45
package/src/app-cache.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { Application, DBManager, Rules, Version } from '@pwrdrvr/microapps-datalib';
|
|
2
|
+
|
|
3
|
+
interface ICacheEntry<T> {
|
|
4
|
+
timestamp: number;
|
|
5
|
+
data: T;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class AppVersionCache {
|
|
9
|
+
private negativeAppNameCache: Map<string, ICacheEntry<undefined>>;
|
|
10
|
+
private appRulesCache: Map<string, ICacheEntry<Rules>>;
|
|
11
|
+
private appVersionsCache: Map<string, Map<string, ICacheEntry<Version>>>;
|
|
12
|
+
private dbManager: DBManager;
|
|
13
|
+
|
|
14
|
+
private static instance: AppVersionCache;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the AppVersionCache instance or create it
|
|
18
|
+
*/
|
|
19
|
+
public static GetInstance({ dbManager }: { dbManager: DBManager }): AppVersionCache {
|
|
20
|
+
if (!AppVersionCache.instance) {
|
|
21
|
+
AppVersionCache.instance = new AppVersionCache({ dbManager });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return AppVersionCache.instance;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor({ dbManager }: { dbManager: DBManager }) {
|
|
28
|
+
this.negativeAppNameCache = new Map();
|
|
29
|
+
this.appRulesCache = new Map();
|
|
30
|
+
this.appVersionsCache = new Map();
|
|
31
|
+
this.dbManager = dbManager;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private get CacheIsEmpty(): boolean {
|
|
35
|
+
return this.appRulesCache.size === 0 && this.appVersionsCache.size === 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Used to populate the cache when the cache is completely empty
|
|
40
|
+
* @param appName
|
|
41
|
+
*/
|
|
42
|
+
private async PopulateEmptyCache({
|
|
43
|
+
key: { AppName },
|
|
44
|
+
}: {
|
|
45
|
+
key: { AppName: string };
|
|
46
|
+
}): Promise<void> {
|
|
47
|
+
const versionsAndRules = await Application.GetVersionsAndRules({
|
|
48
|
+
dbManager: this.dbManager,
|
|
49
|
+
key: { AppName },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!versionsAndRules || (versionsAndRules.Versions.length === 0 && !versionsAndRules.Rules)) {
|
|
53
|
+
this.negativeAppNameCache.set(AppName.toLowerCase(), {
|
|
54
|
+
timestamp: Date.now(),
|
|
55
|
+
data: undefined,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Remove negative cache entry if it exists
|
|
61
|
+
this.negativeAppNameCache.delete(AppName.toLowerCase());
|
|
62
|
+
|
|
63
|
+
this.appRulesCache.set(AppName.toLowerCase(), {
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
data: versionsAndRules.Rules,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const versionsMap = new Map<string, ICacheEntry<Version>>();
|
|
69
|
+
for (const version of versionsAndRules.Versions) {
|
|
70
|
+
versionsMap.set(version.SemVer, {
|
|
71
|
+
timestamp: Date.now(),
|
|
72
|
+
data: version,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
this.appVersionsCache.set(AppName.toLowerCase(), versionsMap);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public ClearCache(): void {
|
|
79
|
+
this.appRulesCache.clear();
|
|
80
|
+
this.appVersionsCache.clear();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get Rules for an app
|
|
85
|
+
*
|
|
86
|
+
* Populates the cache if empty
|
|
87
|
+
* Freshens a cache entry if it is stale
|
|
88
|
+
* Feturns the cached value if it is fresh
|
|
89
|
+
* Fetches a single item if the cache is not empty but the item is not in the cache
|
|
90
|
+
*
|
|
91
|
+
* @param appName
|
|
92
|
+
* @returns
|
|
93
|
+
*/
|
|
94
|
+
public async GetRules({
|
|
95
|
+
key: { AppName },
|
|
96
|
+
}: {
|
|
97
|
+
key: { AppName: string };
|
|
98
|
+
}): Promise<Rules | undefined> {
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
|
|
101
|
+
// Check the negative cache first
|
|
102
|
+
const negativeCacheEntry = this.negativeAppNameCache.get(AppName.toLowerCase());
|
|
103
|
+
if (negativeCacheEntry && now - negativeCacheEntry.timestamp < 60000) {
|
|
104
|
+
return undefined;
|
|
105
|
+
} else if (negativeCacheEntry) {
|
|
106
|
+
this.negativeAppNameCache.delete(AppName.toLowerCase());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const ruleCacheEntry = this.appRulesCache.get(AppName.toLowerCase());
|
|
110
|
+
if (ruleCacheEntry && now - ruleCacheEntry.timestamp < 60000) {
|
|
111
|
+
return ruleCacheEntry.data;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (this.CacheIsEmpty || !ruleCacheEntry) {
|
|
115
|
+
await this.PopulateEmptyCache({ key: { AppName } });
|
|
116
|
+
|
|
117
|
+
return this.appRulesCache.get(AppName.toLowerCase())?.data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const versionsAndRules = await Application.GetVersionsAndRules({
|
|
121
|
+
dbManager: this.dbManager,
|
|
122
|
+
key: { AppName: AppName },
|
|
123
|
+
});
|
|
124
|
+
this.appRulesCache.set(AppName.toLowerCase(), { timestamp: now, data: versionsAndRules.Rules });
|
|
125
|
+
|
|
126
|
+
return versionsAndRules.Rules;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get Version info for an app and semVer
|
|
131
|
+
*
|
|
132
|
+
* Populates the cache if empty
|
|
133
|
+
* Freshens a cache entry if it is stale
|
|
134
|
+
* Feturns the cached value if it is fresh
|
|
135
|
+
* Fetches a single item if the cache is not empty but the item is not in the cache
|
|
136
|
+
*
|
|
137
|
+
* @param appName
|
|
138
|
+
* @returns
|
|
139
|
+
*/
|
|
140
|
+
public async GetVersionInfo({
|
|
141
|
+
key: { AppName, SemVer },
|
|
142
|
+
}: {
|
|
143
|
+
key: { AppName: string; SemVer: string };
|
|
144
|
+
}): Promise<Version | undefined> {
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
|
|
147
|
+
// Check the negative cache first
|
|
148
|
+
const negativeCacheEntry = this.negativeAppNameCache.get(AppName.toLowerCase());
|
|
149
|
+
if (negativeCacheEntry && now - negativeCacheEntry.timestamp < 60000) {
|
|
150
|
+
return undefined;
|
|
151
|
+
} else if (negativeCacheEntry) {
|
|
152
|
+
this.negativeAppNameCache.delete(AppName.toLowerCase());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Check if we have the item cached and if it is fresh enough
|
|
156
|
+
const versionCacheEntry = this.appVersionsCache.get(AppName.toLowerCase());
|
|
157
|
+
if (versionCacheEntry) {
|
|
158
|
+
const versionInfo = versionCacheEntry.get(SemVer);
|
|
159
|
+
if (versionInfo && now - versionInfo.timestamp < 900000) {
|
|
160
|
+
return versionInfo.data;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (this.CacheIsEmpty || !versionCacheEntry) {
|
|
165
|
+
await this.PopulateEmptyCache({ key: { AppName } });
|
|
166
|
+
if (!this.appVersionsCache) {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return this.appVersionsCache.get(AppName.toLowerCase())?.get(SemVer)?.data;
|
|
171
|
+
} else {
|
|
172
|
+
// We have some data in the cache, but not for this version
|
|
173
|
+
// So we need to get the data for this version
|
|
174
|
+
const versionInfo = await Version.LoadVersion({
|
|
175
|
+
dbManager: this.dbManager,
|
|
176
|
+
key: { AppName, SemVer: SemVer },
|
|
177
|
+
});
|
|
178
|
+
if (!versionInfo) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const versionsMap = this.appVersionsCache.get(AppName.toLowerCase());
|
|
183
|
+
if (!versionsMap) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Save the version info in the cache
|
|
188
|
+
versionsMap.set(SemVer, { timestamp: now, data: versionInfo });
|
|
189
|
+
|
|
190
|
+
return versionInfo;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
package/src/index.spec.ts
CHANGED
|
@@ -97,7 +97,7 @@ describe('router - without prefix', () => {
|
|
|
97
97
|
const version = new Version({
|
|
98
98
|
AppName: 'Bat',
|
|
99
99
|
IntegrationID: 'abcd',
|
|
100
|
-
SemVer: '3.2.1-beta.
|
|
100
|
+
SemVer: '3.2.1-beta.2',
|
|
101
101
|
Status: 'deployed',
|
|
102
102
|
Type: 'static',
|
|
103
103
|
});
|
|
@@ -106,17 +106,17 @@ describe('router - without prefix', () => {
|
|
|
106
106
|
const rules = new Rules({
|
|
107
107
|
AppName: 'Bat',
|
|
108
108
|
Version: 0,
|
|
109
|
-
RuleSet: { default: { SemVer: '3.2.1-beta.
|
|
109
|
+
RuleSet: { default: { SemVer: '3.2.1-beta.2', AttributeName: '', AttributeValue: '' } },
|
|
110
110
|
});
|
|
111
111
|
await rules.Save(dbManager);
|
|
112
112
|
|
|
113
113
|
// Call the handler
|
|
114
|
-
const response = await GetRoute({ dbManager, rawPath: '/bat/3.2.1-beta.
|
|
114
|
+
const response = await GetRoute({ dbManager, rawPath: '/bat/3.2.1-beta.2/' });
|
|
115
115
|
|
|
116
116
|
expect(response).toHaveProperty('appName');
|
|
117
117
|
expect(response.appName).toBe('bat');
|
|
118
118
|
expect(response).toHaveProperty('semVer');
|
|
119
|
-
expect(response.semVer).toBe('3.2.1-beta.
|
|
119
|
+
expect(response.semVer).toBe('3.2.1-beta.2');
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it('static app - request to app/x.y.z/ should redirect to defaultFile', async () => {
|
|
@@ -153,14 +153,15 @@ describe('router - without prefix', () => {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
it('static app - request to app/notVersion should load app frame with defaultFile', async () => {
|
|
156
|
+
const AppName = 'Bat123';
|
|
156
157
|
const app = new Application({
|
|
157
|
-
AppName
|
|
158
|
+
AppName,
|
|
158
159
|
DisplayName: 'Bat App',
|
|
159
160
|
});
|
|
160
161
|
await app.Save(dbManager);
|
|
161
162
|
|
|
162
163
|
const version = new Version({
|
|
163
|
-
AppName
|
|
164
|
+
AppName,
|
|
164
165
|
DefaultFile: 'bat.html',
|
|
165
166
|
IntegrationID: 'abcd',
|
|
166
167
|
SemVer: '3.2.1-beta.1',
|
|
@@ -170,30 +171,31 @@ describe('router - without prefix', () => {
|
|
|
170
171
|
await version.Save(dbManager);
|
|
171
172
|
|
|
172
173
|
const rules = new Rules({
|
|
173
|
-
AppName
|
|
174
|
+
AppName,
|
|
174
175
|
Version: 0,
|
|
175
176
|
RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
|
|
176
177
|
});
|
|
177
178
|
await rules.Save(dbManager);
|
|
178
179
|
|
|
179
180
|
// Call the handler
|
|
180
|
-
const response = await GetRoute({ dbManager, rawPath:
|
|
181
|
+
const response = await GetRoute({ dbManager, rawPath: `/${AppName}/notVersion` });
|
|
181
182
|
|
|
182
183
|
expect(response).toHaveProperty('statusCode');
|
|
183
184
|
expect(response.statusCode).toBe(200);
|
|
184
185
|
expect(response).toBeDefined();
|
|
185
|
-
expect(response.iFrameAppVersionPath).toBe(
|
|
186
|
+
expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta.1/bat.html`);
|
|
186
187
|
});
|
|
187
188
|
|
|
188
189
|
it('should serve appframe with no default file', async () => {
|
|
190
|
+
const AppName = 'Bat124';
|
|
189
191
|
const app = new Application({
|
|
190
|
-
AppName
|
|
192
|
+
AppName,
|
|
191
193
|
DisplayName: 'Bat App',
|
|
192
194
|
});
|
|
193
195
|
await app.Save(dbManager);
|
|
194
196
|
|
|
195
197
|
const version = new Version({
|
|
196
|
-
AppName
|
|
198
|
+
AppName,
|
|
197
199
|
DefaultFile: '',
|
|
198
200
|
IntegrationID: 'abcd',
|
|
199
201
|
SemVer: '3.2.1-beta1',
|
|
@@ -203,30 +205,31 @@ describe('router - without prefix', () => {
|
|
|
203
205
|
await version.Save(dbManager);
|
|
204
206
|
|
|
205
207
|
const rules = new Rules({
|
|
206
|
-
AppName
|
|
208
|
+
AppName,
|
|
207
209
|
Version: 0,
|
|
208
210
|
RuleSet: { default: { SemVer: '3.2.1-beta1', AttributeName: '', AttributeValue: '' } },
|
|
209
211
|
});
|
|
210
212
|
await rules.Save(dbManager);
|
|
211
213
|
|
|
212
214
|
// Call the handler
|
|
213
|
-
const response = await GetRoute({ dbManager, rawPath:
|
|
215
|
+
const response = await GetRoute({ dbManager, rawPath: `/${AppName}/` });
|
|
214
216
|
|
|
215
217
|
expect(response).toBeDefined();
|
|
216
218
|
expect(response).toHaveProperty('statusCode');
|
|
217
219
|
expect(response.statusCode).toBe(200);
|
|
218
|
-
expect(response.iFrameAppVersionPath).toBe(
|
|
220
|
+
expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta1`);
|
|
219
221
|
});
|
|
220
222
|
|
|
221
223
|
it('should serve appframe with sub-route', async () => {
|
|
224
|
+
const AppName = 'Bat125';
|
|
222
225
|
const app = new Application({
|
|
223
|
-
AppName
|
|
226
|
+
AppName,
|
|
224
227
|
DisplayName: 'Bat App',
|
|
225
228
|
});
|
|
226
229
|
await app.Save(dbManager);
|
|
227
230
|
|
|
228
231
|
const version = new Version({
|
|
229
|
-
AppName
|
|
232
|
+
AppName,
|
|
230
233
|
DefaultFile: '',
|
|
231
234
|
IntegrationID: 'abcd',
|
|
232
235
|
SemVer: '3.2.1-beta2',
|
|
@@ -236,30 +239,31 @@ describe('router - without prefix', () => {
|
|
|
236
239
|
await version.Save(dbManager);
|
|
237
240
|
|
|
238
241
|
const rules = new Rules({
|
|
239
|
-
AppName
|
|
242
|
+
AppName,
|
|
240
243
|
Version: 0,
|
|
241
244
|
RuleSet: { default: { SemVer: '3.2.1-beta2', AttributeName: '', AttributeValue: '' } },
|
|
242
245
|
});
|
|
243
246
|
await rules.Save(dbManager);
|
|
244
247
|
|
|
245
248
|
// Call the handler
|
|
246
|
-
const response = await GetRoute({ dbManager, rawPath:
|
|
249
|
+
const response = await GetRoute({ dbManager, rawPath: `/${AppName}/demo/grid` });
|
|
247
250
|
|
|
248
251
|
expect(response).toBeDefined();
|
|
249
252
|
expect(response).toHaveProperty('statusCode');
|
|
250
253
|
expect(response.statusCode).toBe(200);
|
|
251
|
-
expect(response.iFrameAppVersionPath).toBe(
|
|
254
|
+
expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta2/demo/grid`);
|
|
252
255
|
});
|
|
253
256
|
|
|
254
257
|
it('should serve appframe with sub-route', async () => {
|
|
258
|
+
const AppName = 'Bat126';
|
|
255
259
|
const app = new Application({
|
|
256
|
-
AppName
|
|
260
|
+
AppName,
|
|
257
261
|
DisplayName: 'Bat App',
|
|
258
262
|
});
|
|
259
263
|
await app.Save(dbManager);
|
|
260
264
|
|
|
261
265
|
const version = new Version({
|
|
262
|
-
AppName
|
|
266
|
+
AppName,
|
|
263
267
|
DefaultFile: 'someFile.html',
|
|
264
268
|
IntegrationID: 'abcd',
|
|
265
269
|
SemVer: '3.2.1-beta3',
|
|
@@ -269,19 +273,19 @@ describe('router - without prefix', () => {
|
|
|
269
273
|
await version.Save(dbManager);
|
|
270
274
|
|
|
271
275
|
const rules = new Rules({
|
|
272
|
-
AppName
|
|
276
|
+
AppName,
|
|
273
277
|
Version: 0,
|
|
274
278
|
RuleSet: { default: { SemVer: '3.2.1-beta3', AttributeName: '', AttributeValue: '' } },
|
|
275
279
|
});
|
|
276
280
|
await rules.Save(dbManager);
|
|
277
281
|
|
|
278
282
|
// Call the handler
|
|
279
|
-
const response = await GetRoute({ dbManager, rawPath:
|
|
283
|
+
const response = await GetRoute({ dbManager, rawPath: `/${AppName}/demo` });
|
|
280
284
|
|
|
281
285
|
expect(response).toBeDefined();
|
|
282
286
|
expect(response).toHaveProperty('statusCode');
|
|
283
287
|
expect(response.statusCode).toBe(200);
|
|
284
|
-
expect(response.iFrameAppVersionPath).toBe(
|
|
288
|
+
expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta3/demo`);
|
|
285
289
|
});
|
|
286
290
|
|
|
287
291
|
it('should return 404 for /favicon.ico', async () => {
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
// Used by ts-convict
|
|
2
1
|
import 'source-map-support/register';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { pathExistsSync, readFileSync } from 'fs-extra';
|
|
5
|
-
import {
|
|
4
|
+
import { DBManager, Rules, Version } from '@pwrdrvr/microapps-datalib';
|
|
6
5
|
import Log from './lib/log';
|
|
6
|
+
import { AppVersionCache } from './app-cache';
|
|
7
7
|
|
|
8
8
|
const log = Log.Instance;
|
|
9
9
|
|
|
10
|
+
export { AppVersionCache };
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* Find and load the appFrame file
|
|
12
14
|
* @returns
|
|
@@ -159,10 +161,13 @@ export async function GetRoute(event: IGetRouteEvent): Promise<IGetRouteResult>
|
|
|
159
161
|
// /someapp/somepath/somefile.foo will split into length 4 with ["", "someapp", "somepath", "somefile.foo", ""] as results
|
|
160
162
|
const partsAfterPrefix = pathAfterPrefix.split('/');
|
|
161
163
|
|
|
162
|
-
const
|
|
164
|
+
const appName = await GetAppInfo({
|
|
163
165
|
dbManager,
|
|
164
166
|
appName: partsAfterPrefix.length >= 2 ? partsAfterPrefix[1] : '[root]',
|
|
165
167
|
});
|
|
168
|
+
if (!appName) {
|
|
169
|
+
return { statusCode: 404, errorMessage: 'App not found' };
|
|
170
|
+
}
|
|
166
171
|
|
|
167
172
|
const isRootApp = appName === '[root]';
|
|
168
173
|
const appNameOrRootTrailingSlash = isRootApp ? '' : `${appName}/`;
|
|
@@ -215,7 +220,7 @@ export async function GetRoute(event: IGetRouteEvent): Promise<IGetRouteResult>
|
|
|
215
220
|
// ^ ^^^^^^^^^^^^
|
|
216
221
|
// 0 1
|
|
217
222
|
// Got at least an application name, try to route it
|
|
218
|
-
const response = RouteApp({
|
|
223
|
+
const response = await RouteApp({
|
|
219
224
|
dbManager,
|
|
220
225
|
normalizedPathPrefix,
|
|
221
226
|
event,
|
|
@@ -224,7 +229,6 @@ export async function GetRoute(event: IGetRouteEvent): Promise<IGetRouteResult>
|
|
|
224
229
|
possibleSemVerPathAfterApp,
|
|
225
230
|
possibleSemVerQuery: queryStringParameters?.get('appver') || '',
|
|
226
231
|
additionalParts,
|
|
227
|
-
versionsAndRules,
|
|
228
232
|
appNameOrRootTrailingSlash,
|
|
229
233
|
});
|
|
230
234
|
if (response) {
|
|
@@ -244,44 +248,32 @@ export async function GetRoute(event: IGetRouteEvent): Promise<IGetRouteResult>
|
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
|
|
247
|
-
export interface IAppInfo {
|
|
248
|
-
appName: string;
|
|
249
|
-
versionsAndRules: IVersionsAndRules;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
251
|
/**
|
|
253
|
-
*
|
|
252
|
+
* Determine if we have an appname or a catch all app
|
|
254
253
|
*/
|
|
255
254
|
export async function GetAppInfo(opts: {
|
|
256
255
|
dbManager: DBManager;
|
|
257
256
|
appName: string;
|
|
258
|
-
}): Promise<
|
|
257
|
+
}): Promise<string | undefined> {
|
|
259
258
|
const { dbManager, appName } = opts;
|
|
260
259
|
|
|
261
|
-
let
|
|
260
|
+
let rules: Rules | undefined;
|
|
262
261
|
|
|
263
|
-
|
|
264
|
-
dbManager,
|
|
265
|
-
key: { AppName: appName },
|
|
266
|
-
});
|
|
262
|
+
const appVersionCache = AppVersionCache.GetInstance({ dbManager });
|
|
267
263
|
|
|
268
|
-
if
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
};
|
|
264
|
+
// Check if we got a matching app name
|
|
265
|
+
rules = await appVersionCache.GetRules({ key: { AppName: appName } });
|
|
266
|
+
if (rules && rules.AppName === appName.toLowerCase()) {
|
|
267
|
+
return appName;
|
|
273
268
|
}
|
|
274
269
|
|
|
275
270
|
// Check if we have a `[root]` app that is a catch all
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
271
|
+
rules = await appVersionCache.GetRules({ key: { AppName: '[root]' } });
|
|
272
|
+
if (rules && rules.AppName === '[root]') {
|
|
273
|
+
return '[root]';
|
|
274
|
+
}
|
|
280
275
|
|
|
281
|
-
return
|
|
282
|
-
appName: '[root]',
|
|
283
|
-
versionsAndRules,
|
|
284
|
-
};
|
|
276
|
+
return undefined;
|
|
285
277
|
}
|
|
286
278
|
|
|
287
279
|
/**
|
|
@@ -293,7 +285,7 @@ export async function GetAppInfo(opts: {
|
|
|
293
285
|
* @param log
|
|
294
286
|
* @returns
|
|
295
287
|
*/
|
|
296
|
-
function RouteApp(opts: {
|
|
288
|
+
async function RouteApp(opts: {
|
|
297
289
|
dbManager: DBManager;
|
|
298
290
|
event: IGetRouteEvent;
|
|
299
291
|
appName: string;
|
|
@@ -302,10 +294,10 @@ function RouteApp(opts: {
|
|
|
302
294
|
possibleSemVerQuery?: string;
|
|
303
295
|
additionalParts: string;
|
|
304
296
|
normalizedPathPrefix?: string;
|
|
305
|
-
versionsAndRules: IVersionsAndRules;
|
|
306
297
|
appNameOrRootTrailingSlash: string;
|
|
307
|
-
}): IGetRouteResult {
|
|
298
|
+
}): Promise<IGetRouteResult> {
|
|
308
299
|
const {
|
|
300
|
+
dbManager,
|
|
309
301
|
event,
|
|
310
302
|
normalizedPathPrefix = '',
|
|
311
303
|
appName,
|
|
@@ -313,21 +305,28 @@ function RouteApp(opts: {
|
|
|
313
305
|
possibleSemVerPathAfterApp,
|
|
314
306
|
possibleSemVerQuery,
|
|
315
307
|
additionalParts,
|
|
316
|
-
versionsAndRules,
|
|
317
308
|
appNameOrRootTrailingSlash,
|
|
318
309
|
} = opts;
|
|
319
310
|
|
|
320
311
|
let versionInfoToUse: Version | undefined;
|
|
321
312
|
|
|
313
|
+
const appVersionCache = AppVersionCache.GetInstance({ dbManager });
|
|
314
|
+
|
|
322
315
|
// Check if the semver placeholder is actually a defined version
|
|
323
316
|
const possibleSemVerPathAfterAppVersionInfo = possibleSemVerPathAfterApp
|
|
324
|
-
?
|
|
317
|
+
? await appVersionCache.GetVersionInfo({
|
|
318
|
+
key: { AppName: appName, SemVer: possibleSemVerPathAfterApp },
|
|
319
|
+
})
|
|
325
320
|
: undefined;
|
|
326
321
|
const possibleSemVerPathNextDataVersionInfo = possibleSemVerPathNextData
|
|
327
|
-
?
|
|
322
|
+
? await appVersionCache.GetVersionInfo({
|
|
323
|
+
key: { AppName: appName, SemVer: possibleSemVerPathNextData },
|
|
324
|
+
})
|
|
328
325
|
: undefined;
|
|
329
326
|
const possibleSemVerQueryVersionInfo = possibleSemVerQuery
|
|
330
|
-
?
|
|
327
|
+
? await appVersionCache.GetVersionInfo({
|
|
328
|
+
key: { AppName: appName, SemVer: possibleSemVerQuery },
|
|
329
|
+
})
|
|
331
330
|
: undefined;
|
|
332
331
|
|
|
333
332
|
// If there is a version in the path, use it
|
|
@@ -377,7 +376,8 @@ function RouteApp(opts: {
|
|
|
377
376
|
// 80% to 1.1.0, 20% to default (1.0.3)
|
|
378
377
|
//
|
|
379
378
|
|
|
380
|
-
const
|
|
379
|
+
const rules = await appVersionCache.GetRules({ key: { AppName: appName } });
|
|
380
|
+
const defaultVersion = rules?.RuleSet.default?.SemVer;
|
|
381
381
|
|
|
382
382
|
if (defaultVersion == null) {
|
|
383
383
|
log.error(`could not find app ${appName}, for path ${event.rawPath} - returning 404`, {
|
|
@@ -390,10 +390,9 @@ function RouteApp(opts: {
|
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
);
|
|
393
|
+
const defaultVersionInfo = await appVersionCache.GetVersionInfo({
|
|
394
|
+
key: { AppName: appName, SemVer: defaultVersion },
|
|
395
|
+
});
|
|
397
396
|
|
|
398
397
|
versionInfoToUse = defaultVersionInfo;
|
|
399
398
|
}
|
|
@@ -487,11 +486,13 @@ async function RedirectToDefaultFile(opts: {
|
|
|
487
486
|
appNameOrRootTrailingSlash,
|
|
488
487
|
semVer,
|
|
489
488
|
} = opts;
|
|
490
|
-
let versionInfo: Version;
|
|
489
|
+
let versionInfo: Version | undefined;
|
|
491
490
|
|
|
492
491
|
try {
|
|
493
|
-
|
|
494
|
-
|
|
492
|
+
// Get the cache
|
|
493
|
+
const appVersionCache = AppVersionCache.GetInstance({ dbManager });
|
|
494
|
+
|
|
495
|
+
versionInfo = await appVersionCache.GetVersionInfo({
|
|
495
496
|
key: { AppName: appName, SemVer: semVer },
|
|
496
497
|
});
|
|
497
498
|
} catch (error) {
|