@pwrdrvr/microapps-router-lib 1.2.0-beta.5 → 1.2.0-beta.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pwrdrvr/microapps-router-lib",
3
- "version": "1.2.0-beta.5",
3
+ "version": "1.2.0-beta.8",
4
4
  "description": "Router library for the microapps framework",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
@@ -15,15 +15,15 @@
15
15
  },
16
16
  "homepage": "https://github.com/pwrdrvr/microapps-core#readme",
17
17
  "dependencies": {
18
- "@aws-sdk/client-dynamodb": "^3.78.0",
18
+ "@aws-sdk/client-dynamodb": "^3.1024.0",
19
19
  "fs-extra": "^9.0.0",
20
20
  "lambda-log": "^3.0.0",
21
21
  "source-map-support": "^0.5.0",
22
22
  "tslib": "^2.1.0",
23
- "@pwrdrvr/microapps-datalib": "1.2.0-beta.5"
23
+ "@pwrdrvr/microapps-datalib": "1.2.0-beta.8"
24
24
  },
25
25
  "devDependencies": {
26
- "@aws-sdk/client-dynamodb": "3.78.0",
26
+ "@aws-sdk/client-dynamodb": "3.1024.0",
27
27
  "@types/aws-lambda": "8.10.110",
28
28
  "@types/fs-extra": "^9.0.13",
29
29
  "@types/lambda-log": "^2.2.0",
@@ -34,12 +34,14 @@
34
34
  "jest-dynalite": "3.6.1",
35
35
  "lambda-log": "^3.1.0",
36
36
  "mock-fs": "5.5.0",
37
+ "reflect-metadata": "^0.1.13",
37
38
  "source-map-support": "0.5.21",
38
39
  "tslib": "^2.1.0"
39
40
  },
40
41
  "files": [
41
42
  "dist",
42
43
  "src",
44
+ "!src/**/*.spec.ts",
43
45
  "package.json",
44
46
  "LICENSE"
45
47
  ],
@@ -1,175 +0,0 @@
1
- /// <reference types="jest" />
2
- import 'jest-dynalite/withDb';
3
- import * as dynamodb from '@aws-sdk/client-dynamodb';
4
- import { Application, DBManager, Version, Rules } from '@pwrdrvr/microapps-datalib';
5
- import { AppVersionCache } from './app-cache';
6
-
7
- let dynamoClient: dynamodb.DynamoDBClient;
8
- let dbManager: DBManager;
9
-
10
- const TEST_TABLE_NAME = 'microapps';
11
-
12
- describe('app-cache', () => {
13
- beforeAll(() => {
14
- jest.setTimeout(30000);
15
-
16
- dynamoClient = new dynamodb.DynamoDBClient({
17
- endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
18
- tls: false,
19
- region: 'local',
20
- });
21
-
22
- // Init the DB manager to point it at the right table
23
- dbManager = new DBManager({ dynamoClient, tableName: TEST_TABLE_NAME });
24
- });
25
-
26
- describe('GetRules', () => {
27
- it('Returns cached record after deleted from DB', async () => {
28
- const app = new Application({
29
- AppName: 'Cached',
30
- DisplayName: 'Cached App',
31
- });
32
- await app.Save(dbManager);
33
-
34
- const version = new Version({
35
- AppName: 'Cached',
36
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:cached',
37
- URL: 'https://some-lambda-function-url-id.lambda-url.us-east-2.on.aws/',
38
- SemVer: '3.2.1-beta.1',
39
- Status: 'deployed',
40
- Type: 'lambda',
41
- });
42
- await version.Save(dbManager);
43
-
44
- const rules = new Rules({
45
- AppName: 'Cached',
46
- Version: 0,
47
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
48
- });
49
- await rules.Save(dbManager);
50
-
51
- const appVersionCache = new AppVersionCache({ dbManager });
52
-
53
- // Seed the cache while record exists
54
- const versionWhenDBExists = await appVersionCache.GetRules({
55
- key: { AppName: 'Cached' },
56
- });
57
-
58
- expect(versionWhenDBExists).toHaveProperty('AppName', 'cached');
59
- expect(versionWhenDBExists).toHaveProperty('RuleSet', {
60
- default: { AttributeName: '', AttributeValue: '', SemVer: '3.2.1-beta.1' },
61
- });
62
-
63
- // Clobber the Rules record
64
- await Application.UpdateDefaultRule({
65
- dbManager,
66
- key: { AppName: 'Cached', SemVer: '9.9.9' },
67
- });
68
-
69
- // Get record from cache after DB record is deleted
70
- const rulesWhenCleared = await appVersionCache.GetRules({
71
- key: { AppName: 'Cached' },
72
- });
73
-
74
- expect(rulesWhenCleared).toEqual(versionWhenDBExists);
75
- });
76
- });
77
-
78
- describe('GetVersionInfo', () => {
79
- it('Returns cached record after deleted from DB', async () => {
80
- const app = new Application({
81
- AppName: 'Cached',
82
- DisplayName: 'Cached App',
83
- });
84
- await app.Save(dbManager);
85
-
86
- const version = new Version({
87
- AppName: 'Cached',
88
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:cached',
89
- URL: 'https://some-lambda-function-url-id.lambda-url.us-east-2.on.aws/',
90
- SemVer: '3.2.1-beta.1',
91
- Status: 'deployed',
92
- Type: 'lambda',
93
- });
94
- await version.Save(dbManager);
95
-
96
- const rules = new Rules({
97
- AppName: 'Cached',
98
- Version: 0,
99
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
100
- });
101
- await rules.Save(dbManager);
102
-
103
- const appVersionCache = new AppVersionCache({ dbManager });
104
-
105
- // Seed the cache while record exists
106
- const versionWhenDBExists = await appVersionCache.GetVersionInfo({
107
- key: { AppName: 'Cached', SemVer: '3.2.1-beta.1' },
108
- });
109
-
110
- expect(versionWhenDBExists).toHaveProperty('AppName', 'cached');
111
- expect(versionWhenDBExists).toHaveProperty(
112
- 'URL',
113
- 'https://some-lambda-function-url-id.lambda-url.us-east-2.on.aws/',
114
- );
115
- expect(versionWhenDBExists).toHaveProperty('SemVer', '3.2.1-beta.1');
116
- expect(versionWhenDBExists).toHaveProperty('Status', 'deployed');
117
- expect(versionWhenDBExists).toHaveProperty('Type', 'lambda');
118
-
119
- // Delete the record
120
- await Version.DeleteVersion({
121
- dbManager,
122
- key: { AppName: 'Cached', SemVer: '3.2.1-beta.1' },
123
- });
124
-
125
- // Get record from cache after DB record is deleted
126
- const versionWhenDBCleared = await appVersionCache.GetVersionInfo({
127
- key: { AppName: 'Cached', SemVer: '3.2.1-beta.1' },
128
- });
129
-
130
- expect(versionWhenDBCleared).toEqual(versionWhenDBExists);
131
- });
132
-
133
- it('Negative Cache', async () => {
134
- const appVersionCache = new AppVersionCache({ dbManager });
135
-
136
- // Seed the negative cache
137
- const versionWhenDBExists = await appVersionCache.GetVersionInfo({
138
- key: { AppName: 'Missing', SemVer: '7.6.8' },
139
- });
140
-
141
- // Confirm record is not in the DB
142
- expect(versionWhenDBExists).toBeUndefined();
143
-
144
- const app = new Application({
145
- AppName: 'Missing',
146
- DisplayName: 'Bat App',
147
- });
148
- await app.Save(dbManager);
149
-
150
- const version = new Version({
151
- AppName: 'Missing',
152
- SemVer: '7.6.8',
153
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:bat',
154
- URL: 'https://some-lambda-function-url-id.lambda-url.us-east-2.on.aws/',
155
- Status: 'deployed',
156
- Type: 'lambda',
157
- });
158
- await version.Save(dbManager);
159
-
160
- const rules = new Rules({
161
- AppName: 'Missing',
162
- Version: 0,
163
- RuleSet: { default: { SemVer: '7.6.8', AttributeName: '', AttributeValue: '' } },
164
- });
165
- await rules.Save(dbManager);
166
-
167
- // Get record from cache after DB record is created - should be hidden by negative cache
168
- const versionWhenDBCreated = await appVersionCache.GetVersionInfo({
169
- key: { AppName: 'Missing', SemVer: '7.6.8' },
170
- });
171
-
172
- expect(versionWhenDBCreated).toBeUndefined();
173
- });
174
- });
175
- });
@@ -1,77 +0,0 @@
1
- import { DBManager, Rules } from '@pwrdrvr/microapps-datalib';
2
- import { AppVersionCache } from './app-cache';
3
- import { GetAppInfo } from './get-app-info';
4
-
5
- jest.mock('@pwrdrvr/microapps-datalib');
6
-
7
- describe('GetAppInfo', () => {
8
- const dbManager = {} as DBManager;
9
- const getAppVersionCacheSpy = jest.spyOn(AppVersionCache, 'GetInstance');
10
-
11
- beforeEach(() => {
12
- jest.clearAllMocks();
13
- });
14
-
15
- afterEach(() => {
16
- getAppVersionCacheSpy.mockReset();
17
- });
18
-
19
- const testCases = [
20
- {
21
- caseName: 'returns the correct app name when it exists',
22
- appName: 'testApp',
23
- mockRules: {
24
- AppName: 'testapp',
25
- Version: 1,
26
- RuleSet: {
27
- default: {
28
- AttributeName: 'default',
29
- AttributeValue: '',
30
- SemVer: '1.0.0',
31
- },
32
- },
33
- } as unknown as Rules,
34
- expected: 'testApp',
35
- },
36
- {
37
- caseName:
38
- 'returns undefined when the app name does not exist and there is no [root] catch-all app',
39
- appName: 'nonexistent',
40
- mockRules: undefined,
41
- expected: undefined,
42
- },
43
- {
44
- caseName:
45
- 'returns [root] when the app name does not exist but there is a [root] catch-all app',
46
- appName: 'nonexistent',
47
- mockRules: {
48
- AppName: '[root]',
49
- RuleSet: {
50
- default: {
51
- AttributeName: 'default',
52
- AttributeValue: '',
53
- SemVer: '9.0.0',
54
- },
55
- } as unknown as Rules,
56
- },
57
- expected: '[root]',
58
- },
59
- ];
60
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
61
- it.each(testCases)('$caseName', async ({ caseName, appName, mockRules, expected }) => {
62
- getAppVersionCacheSpy.mockImplementation(() => {
63
- return {
64
- // eslint-disable-next-line @typescript-eslint/require-await
65
- GetRules: async ({ key }: { key: { AppName: string } }) => {
66
- if (key.AppName === appName || key.AppName === '[root]') {
67
- return mockRules;
68
- }
69
- return undefined;
70
- },
71
- } as unknown as AppVersionCache;
72
- });
73
-
74
- const result = await GetAppInfo({ dbManager, appName });
75
- expect(result).toEqual(expected);
76
- });
77
- });
@@ -1,93 +0,0 @@
1
- /// <reference types="jest" />
2
- import 'reflect-metadata';
3
- import 'jest-dynalite/withDb';
4
- import * as dynamodb from '@aws-sdk/client-dynamodb';
5
- import { Application, DBManager, Version, Rules } from '@pwrdrvr/microapps-datalib';
6
- import { GetRoute } from './index';
7
-
8
- let dynamoClient: dynamodb.DynamoDBClient;
9
- let dbManager: DBManager;
10
-
11
- const TEST_TABLE_NAME = 'microapps';
12
-
13
- describe('router - with prefix', () => {
14
- beforeAll(() => {
15
- dynamoClient = new dynamodb.DynamoDBClient({
16
- endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
17
- tls: false,
18
- region: 'local',
19
- });
20
-
21
- // Init the DB manager to point it at the right table
22
- dbManager = new DBManager({ dynamoClient, tableName: TEST_TABLE_NAME });
23
- });
24
-
25
- it('should serve appframe with version and default file substitued', async () => {
26
- const app = new Application({
27
- AppName: 'Bat',
28
- DisplayName: 'Bat App',
29
- });
30
- await app.Save(dbManager);
31
-
32
- const version = new Version({
33
- AppName: 'Bat',
34
- DefaultFile: 'bat.html',
35
- IntegrationID: 'abcd',
36
- SemVer: '3.2.1-beta.1',
37
- Status: 'deployed',
38
- Type: 'lambda',
39
- });
40
- await version.Save(dbManager);
41
-
42
- const rules = new Rules({
43
- AppName: 'Bat',
44
- Version: 0,
45
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
46
- });
47
- await rules.Save(dbManager);
48
-
49
- // Call the handler
50
- const response = await GetRoute({
51
- dbManager,
52
- normalizedPathPrefix: '/qa',
53
- rawPath: '/qa/bat/',
54
- });
55
-
56
- expect(response).toHaveProperty('statusCode');
57
- expect(response.statusCode).toBe(200);
58
- expect(response).toBeDefined();
59
- expect(response.iFrameAppVersionPath).toBe('/qa/bat/3.2.1-beta.1/bat.html');
60
- });
61
-
62
- it('should 404 appframe with version if the prefix is missing', async () => {
63
- const app = new Application({
64
- AppName: 'Bat',
65
- DisplayName: 'Bat App',
66
- });
67
- await app.Save(dbManager);
68
-
69
- const version = new Version({
70
- AppName: 'Bat',
71
- DefaultFile: 'bat.html',
72
- IntegrationID: 'abcd',
73
- SemVer: '3.2.1-beta.1',
74
- Status: 'deployed',
75
- Type: 'lambda',
76
- });
77
- await version.Save(dbManager);
78
-
79
- const rules = new Rules({
80
- AppName: 'Bat',
81
- Version: 0,
82
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
83
- });
84
- await rules.Save(dbManager);
85
-
86
- // Call the handler
87
- const response = await GetRoute({ dbManager, normalizedPathPrefix: '/qa', rawPath: '/bat/' });
88
-
89
- expect(response).toHaveProperty('statusCode');
90
- expect(response.statusCode).toBe(404);
91
- expect(response.errorMessage).toBe('Request not routable');
92
- });
93
- });
@@ -1,585 +0,0 @@
1
- /// <reference types="jest" />
2
- import 'jest-dynalite/withDb';
3
- import * as dynamodb from '@aws-sdk/client-dynamodb';
4
- import { Application, DBManager, Version, Rules } from '@pwrdrvr/microapps-datalib';
5
- import { GetRoute } from './index';
6
-
7
- let dynamoClient: dynamodb.DynamoDBClient;
8
- let dbManager: DBManager;
9
-
10
- const TEST_TABLE_NAME = 'microapps';
11
-
12
- describe('router - without prefix', () => {
13
- beforeAll(() => {
14
- dynamoClient = new dynamodb.DynamoDBClient({
15
- endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
16
- tls: false,
17
- region: 'local',
18
- });
19
-
20
- // Init the DB manager to point it at the right table
21
- dbManager = new DBManager({ dynamoClient, tableName: TEST_TABLE_NAME });
22
- });
23
-
24
- describe('StartupType: iframe', () => {
25
- it('should serve appframe with version and default file substitued', async () => {
26
- const app = new Application({
27
- AppName: 'Bat',
28
- DisplayName: 'Bat App',
29
- });
30
- await app.Save(dbManager);
31
-
32
- const version = new Version({
33
- AppName: 'Bat',
34
- DefaultFile: 'bat.html',
35
- IntegrationID: 'abcd',
36
- SemVer: '3.2.1-beta.1',
37
- Status: 'deployed',
38
- Type: 'lambda',
39
- });
40
- await version.Save(dbManager);
41
-
42
- const rules = new Rules({
43
- AppName: 'Bat',
44
- Version: 0,
45
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
46
- });
47
- await rules.Save(dbManager);
48
-
49
- // Call the handler
50
- const response = await GetRoute({
51
- dbManager,
52
- rawPath: '/bat/',
53
- });
54
-
55
- expect(response).toHaveProperty('statusCode');
56
- expect(response.statusCode).toBe(200);
57
- expect(response).toBeDefined();
58
- expect(response.iFrameAppVersionPath).toBe('/bat/3.2.1-beta.1/bat.html');
59
- });
60
-
61
- it('should serve appframe with appver query string and default file substitued', async () => {
62
- const AppName = 'BatAppVer';
63
- const app = new Application({
64
- AppName,
65
- DisplayName: 'Bat App',
66
- });
67
- await app.Save(dbManager);
68
-
69
- const version = new Version({
70
- AppName,
71
- DefaultFile: 'bat.html',
72
- IntegrationID: 'abcd',
73
- SemVer: '3.2.1-beta.1',
74
- Status: 'deployed',
75
- Type: 'lambda',
76
- });
77
- await version.Save(dbManager);
78
- const version2 = new Version({
79
- AppName,
80
- DefaultFile: 'bat.html',
81
- IntegrationID: 'abcd',
82
- SemVer: '3.2.1-beta.2',
83
- Status: 'deployed',
84
- Type: 'lambda',
85
- });
86
- await version2.Save(dbManager);
87
-
88
- const rules = new Rules({
89
- AppName,
90
- Version: 0,
91
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
92
- });
93
- await rules.Save(dbManager);
94
-
95
- // Call the handler
96
- const response = await GetRoute({
97
- dbManager,
98
- rawPath: '/batappver/',
99
- queryStringParameters: new URLSearchParams('appver=3.2.1-beta.2'),
100
- });
101
-
102
- expect(response).toHaveProperty('statusCode');
103
- expect(response.statusCode).toBe(200);
104
- expect(response).toBeDefined();
105
- expect(response.iFrameAppVersionPath).toBe('/batappver/3.2.1-beta.2/bat.html');
106
- });
107
-
108
- it('static app - request to app/x.y.z should redirect to defaultFile', async () => {
109
- const app = new Application({
110
- AppName: 'Bat',
111
- DisplayName: 'Bat App',
112
- });
113
- await app.Save(dbManager);
114
-
115
- const version = new Version({
116
- AppName: 'Bat',
117
- DefaultFile: 'bat.html',
118
- IntegrationID: 'abcd',
119
- SemVer: '3.2.1-beta.1',
120
- Status: 'deployed',
121
- Type: 'static',
122
- StartupType: 'iframe',
123
- });
124
- await version.Save(dbManager);
125
-
126
- const rules = new Rules({
127
- AppName: 'Bat',
128
- Version: 0,
129
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
130
- });
131
- await rules.Save(dbManager);
132
-
133
- // Call the handler
134
- const response = await GetRoute({ dbManager, rawPath: '/bat/3.2.1-beta.1' });
135
-
136
- expect(response).toHaveProperty('statusCode');
137
- expect(response.statusCode).toBe(302);
138
- expect(response.redirectLocation).toBeDefined();
139
- expect(response.redirectLocation).toBe('/bat/3.2.1-beta.1/bat.html');
140
- }, 60000);
141
-
142
- it('static app - request to app/x.y.z/ should not redirect if no defaultFile', async () => {
143
- const app = new Application({
144
- AppName: 'Bat',
145
- DisplayName: 'Bat App',
146
- });
147
- await app.Save(dbManager);
148
-
149
- const version = new Version({
150
- AppName: 'Bat',
151
- IntegrationID: 'abcd',
152
- SemVer: '3.2.1-beta.2',
153
- Status: 'deployed',
154
- Type: 'static',
155
- StartupType: 'iframe',
156
- });
157
- await version.Save(dbManager);
158
-
159
- const rules = new Rules({
160
- AppName: 'Bat',
161
- Version: 0,
162
- RuleSet: { default: { SemVer: '3.2.1-beta.2', AttributeName: '', AttributeValue: '' } },
163
- });
164
- await rules.Save(dbManager);
165
-
166
- // Call the handler
167
- const response = await GetRoute({ dbManager, rawPath: '/bat/3.2.1-beta.2/' });
168
-
169
- expect(response).toHaveProperty('appName');
170
- expect(response.appName).toBe('bat');
171
- expect(response).toHaveProperty('semVer');
172
- expect(response.semVer).toBe('3.2.1-beta.2');
173
- });
174
-
175
- it('static app - request to app/some.html should create iframe with path after version', async () => {
176
- const app = new Application({
177
- AppName: 'Bat',
178
- DisplayName: 'Bat App',
179
- });
180
- await app.Save(dbManager);
181
-
182
- const version = new Version({
183
- AppName: 'Bat',
184
- DefaultFile: 'bat.html',
185
- IntegrationID: 'abcd',
186
- SemVer: '3.2.1-beta.1',
187
- Status: 'deployed',
188
- Type: 'static',
189
- StartupType: 'iframe',
190
- });
191
- await version.Save(dbManager);
192
-
193
- const rules = new Rules({
194
- AppName: 'Bat',
195
- Version: 0,
196
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
197
- });
198
- await rules.Save(dbManager);
199
-
200
- // Call the handler
201
- const response = await GetRoute({ dbManager, rawPath: '/bat/some.html' });
202
-
203
- expect(response).toHaveProperty('statusCode');
204
- expect(response.statusCode).toBe(200);
205
- expect(response.redirectLocation).not.toBeDefined();
206
- expect(response.iFrameAppVersionPath).toBe('/bat/3.2.1-beta.1/some.html');
207
- });
208
-
209
- it('static app - request to app/x.y.z/ should redirect to defaultFile', async () => {
210
- const app = new Application({
211
- AppName: 'Bat',
212
- DisplayName: 'Bat App',
213
- });
214
- await app.Save(dbManager);
215
-
216
- const version = new Version({
217
- AppName: 'Bat',
218
- DefaultFile: 'bat.html',
219
- IntegrationID: 'abcd',
220
- SemVer: '3.2.1-beta.1',
221
- Status: 'deployed',
222
- Type: 'static',
223
- StartupType: 'iframe',
224
- });
225
- await version.Save(dbManager);
226
-
227
- const rules = new Rules({
228
- AppName: 'Bat',
229
- Version: 0,
230
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
231
- });
232
- await rules.Save(dbManager);
233
-
234
- // Call the handler
235
- const response = await GetRoute({ dbManager, rawPath: '/bat/3.2.1-beta.1/' });
236
-
237
- expect(response).toHaveProperty('statusCode');
238
- expect(response.statusCode).toBe(302);
239
- expect(response.redirectLocation).toBeDefined();
240
- expect(response.redirectLocation).toBe('/bat/3.2.1-beta.1/bat.html');
241
- });
242
-
243
- it('static app - request to app/notVersion should load app frame with /notVersion suffix', async () => {
244
- const AppName = 'Bat123';
245
- const app = new Application({
246
- AppName,
247
- DisplayName: 'Bat App',
248
- });
249
- await app.Save(dbManager);
250
-
251
- const version = new Version({
252
- AppName,
253
- DefaultFile: 'bat.html',
254
- IntegrationID: 'abcd',
255
- SemVer: '3.2.1-beta.1',
256
- Status: 'deployed',
257
- Type: 'static',
258
- StartupType: 'iframe',
259
- });
260
- await version.Save(dbManager);
261
-
262
- const rules = new Rules({
263
- AppName,
264
- Version: 0,
265
- RuleSet: { default: { SemVer: '3.2.1-beta.1', AttributeName: '', AttributeValue: '' } },
266
- });
267
- await rules.Save(dbManager);
268
-
269
- // Call the handler
270
- const response = await GetRoute({ dbManager, rawPath: `/${AppName}/notVersion` });
271
-
272
- expect(response).toHaveProperty('statusCode');
273
- expect(response.statusCode).toBe(200);
274
- expect(response).toBeDefined();
275
- expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta.1/notVersion`);
276
- });
277
-
278
- it('should serve appframe with no default file', async () => {
279
- const AppName = 'Bat124';
280
- const app = new Application({
281
- AppName,
282
- DisplayName: 'Bat App',
283
- });
284
- await app.Save(dbManager);
285
-
286
- const version = new Version({
287
- AppName,
288
- DefaultFile: '',
289
- IntegrationID: 'abcd',
290
- SemVer: '3.2.1-beta1',
291
- Status: 'deployed',
292
- Type: 'lambda',
293
- StartupType: 'iframe',
294
- });
295
- await version.Save(dbManager);
296
-
297
- const rules = new Rules({
298
- AppName,
299
- Version: 0,
300
- RuleSet: { default: { SemVer: '3.2.1-beta1', AttributeName: '', AttributeValue: '' } },
301
- });
302
- await rules.Save(dbManager);
303
-
304
- // Call the handler
305
- const response = await GetRoute({ dbManager, rawPath: `/${AppName}/` });
306
-
307
- expect(response).toBeDefined();
308
- expect(response).toHaveProperty('statusCode');
309
- expect(response.statusCode).toBe(200);
310
- expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta1`);
311
- });
312
-
313
- it('should serve appframe with sub-route', async () => {
314
- const AppName = 'Bat125';
315
- const app = new Application({
316
- AppName,
317
- DisplayName: 'Bat App',
318
- });
319
- await app.Save(dbManager);
320
-
321
- const version = new Version({
322
- AppName,
323
- DefaultFile: '',
324
- IntegrationID: 'abcd',
325
- SemVer: '3.2.1-beta2',
326
- Status: 'deployed',
327
- Type: 'lambda',
328
- StartupType: 'iframe',
329
- });
330
- await version.Save(dbManager);
331
-
332
- const rules = new Rules({
333
- AppName,
334
- Version: 0,
335
- RuleSet: { default: { SemVer: '3.2.1-beta2', AttributeName: '', AttributeValue: '' } },
336
- });
337
- await rules.Save(dbManager);
338
-
339
- // Call the handler
340
- const response = await GetRoute({ dbManager, rawPath: `/${AppName}/demo/grid` });
341
-
342
- expect(response).toBeDefined();
343
- expect(response).toHaveProperty('statusCode');
344
- expect(response.statusCode).toBe(200);
345
- expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta2/demo/grid`);
346
- });
347
-
348
- it('should serve appframe with sub-route', async () => {
349
- const AppName = 'Bat126';
350
- const app = new Application({
351
- AppName,
352
- DisplayName: 'Bat App',
353
- });
354
- await app.Save(dbManager);
355
-
356
- const version = new Version({
357
- AppName,
358
- DefaultFile: 'someFile.html',
359
- IntegrationID: 'abcd',
360
- SemVer: '3.2.1-beta3',
361
- Status: 'deployed',
362
- Type: 'lambda',
363
- StartupType: 'iframe',
364
- });
365
- await version.Save(dbManager);
366
-
367
- const rules = new Rules({
368
- AppName,
369
- Version: 0,
370
- RuleSet: { default: { SemVer: '3.2.1-beta3', AttributeName: '', AttributeValue: '' } },
371
- });
372
- await rules.Save(dbManager);
373
-
374
- // Call the handler
375
- const response = await GetRoute({ dbManager, rawPath: `/${AppName}/demo` });
376
-
377
- expect(response).toBeDefined();
378
- expect(response).toHaveProperty('statusCode');
379
- expect(response.statusCode).toBe(200);
380
- expect(response.iFrameAppVersionPath).toBe(`/${AppName}/3.2.1-beta3/demo`);
381
- });
382
-
383
- it('should return 404 for /favicon.ico', async () => {
384
- const app = new Application({
385
- AppName: 'Bat',
386
- DisplayName: 'Bat App',
387
- });
388
- await app.Save(dbManager);
389
-
390
- const version = new Version({
391
- AppName: 'Bat',
392
- DefaultFile: 'someFile.html',
393
- IntegrationID: 'abcd',
394
- SemVer: '3.2.1-beta3',
395
- Status: 'deployed',
396
- Type: 'lambda',
397
- StartupType: 'iframe',
398
- });
399
- await version.Save(dbManager);
400
-
401
- const rules = new Rules({
402
- AppName: 'Bat',
403
- Version: 0,
404
- RuleSet: { default: { SemVer: '3.2.1-beta3', AttributeName: '', AttributeValue: '' } },
405
- });
406
- await rules.Save(dbManager);
407
-
408
- // Call the handler
409
- const response = await GetRoute({ dbManager, rawPath: '/favicon.ico' });
410
-
411
- expect(response).toBeDefined();
412
- expect(response).toHaveProperty('statusCode');
413
- expect(response.statusCode).toBe(404);
414
- });
415
- });
416
-
417
- describe('StartupType: direct', () => {
418
- it('should serve direct app with sub-route', async () => {
419
- const AppName = 'DirectBat126';
420
- const app = new Application({
421
- AppName,
422
- DisplayName: 'Bat App',
423
- });
424
- await app.Save(dbManager);
425
-
426
- const version = new Version({
427
- AppName,
428
- DefaultFile: 'someFile.html',
429
- IntegrationID: 'abcd',
430
- SemVer: '3.2.1-beta3',
431
- Status: 'deployed',
432
- Type: 'lambda-url',
433
- StartupType: 'direct',
434
- URL: 'https://abc123.lambda-url.us-east-1.on.aws',
435
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:my-function',
436
- });
437
- await version.Save(dbManager);
438
-
439
- const rules = new Rules({
440
- AppName,
441
- Version: 0,
442
- RuleSet: { default: { SemVer: '3.2.1-beta3', AttributeName: '', AttributeValue: '' } },
443
- });
444
- await rules.Save(dbManager);
445
-
446
- // Call the handler
447
- const response = await GetRoute({ dbManager, rawPath: `/${AppName}/demo` });
448
-
449
- expect(response).toBeDefined();
450
- expect(response).toHaveProperty('statusCode');
451
- expect(response.statusCode).toBe(200);
452
- expect(response.appName).toBe(AppName);
453
- expect(response.semVer).toBe('3.2.1-beta3');
454
- expect(response.url).toBe('https://abc123.lambda-url.us-east-1.on.aws');
455
- });
456
-
457
- describe('/_next/data/ no-basePath', () => {
458
- const testCases = [
459
- {
460
- AppName: 'DirectBatNextData',
461
- SemVer: '3.2.1-beta3',
462
- Locales: ['en'],
463
- LambdaFunctionURL: 'https://abc123.lambda-url.us-east-1.on.aws',
464
- RawPath: '/_next/data/3.2.1-beta3/directbatnextdata/demo.json',
465
- },
466
- {
467
- AppName: 'AnotherAppName',
468
- SemVer: '1.0.0',
469
- Locales: ['en'],
470
- LambdaFunctionURL: 'https://abc124.lambda-url.us-east-1.on.aws',
471
- RawPath: '/_next/data/1.0.0/anotherappname.json',
472
- },
473
- {
474
- AppName: 'AppWithLocales',
475
- SemVer: '1.0.0',
476
- Locales: ['en', 'sv'],
477
- LambdaFunctionURL: 'https://abc125.lambda-url.us-east-1.on.aws',
478
- RawPath: '/_next/data/1.0.0/sv/appwithlocales.json',
479
- },
480
- {
481
- AppName: 'ComplicatedLocales',
482
- SemVer: '1.0.0',
483
- Locales: ['en', 'en-US', 'sv', 'zh-Hant'],
484
- LambdaFunctionURL: 'https://abc125.lambda-url.us-east-1.on.aws',
485
- RawPath: '/_next/data/1.0.0/en-US/complicatedlocales.json',
486
- },
487
- {
488
- AppName: 'ComplicatedLocales',
489
- SemVer: '1.0.0',
490
- Locales: ['en', 'en-US', 'sv', 'zh-Hant'],
491
- LambdaFunctionURL: 'https://abc125.lambda-url.us-east-1.on.aws',
492
- RawPath: '/_next/data/1.0.0/en-US/complicatedlocales/index.json',
493
- },
494
- // Add more test cases as needed
495
- ];
496
-
497
- it.each(testCases)(
498
- 'should serve direct app with $RawPath route',
499
- async ({ AppName, SemVer, LambdaFunctionURL, Locales = [], RawPath }) => {
500
- const app = new Application({
501
- AppName,
502
- DisplayName: 'Bat App',
503
- });
504
- await app.Save(dbManager);
505
-
506
- const version = new Version({
507
- AppName,
508
- DefaultFile: 'someFile.html',
509
- IntegrationID: 'abcd',
510
- SemVer,
511
- Status: 'deployed',
512
- Type: 'lambda-url',
513
- StartupType: 'direct',
514
- URL: LambdaFunctionURL,
515
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:my-function',
516
- });
517
- await version.Save(dbManager);
518
-
519
- const rules = new Rules({
520
- AppName,
521
- Version: 0,
522
- RuleSet: { default: { SemVer, AttributeName: '', AttributeValue: '' } },
523
- });
524
- await rules.Save(dbManager);
525
-
526
- // Call the handler
527
- const response = await GetRoute({
528
- dbManager,
529
- locales: Locales,
530
- rawPath: RawPath,
531
- });
532
-
533
- expect(response).toBeDefined();
534
- expect(response).toHaveProperty('statusCode');
535
- expect(response.appName).toBe(AppName.toLowerCase());
536
- expect(response.semVer).toBe(SemVer);
537
- expect(response.statusCode).toBe(200);
538
- expect(response.url).toBe(LambdaFunctionURL);
539
- },
540
- );
541
- });
542
-
543
- it('should serve direct app with /_next/data/[semver]/[appname]/route route', async () => {
544
- const AppName = 'DirectBatNextData';
545
- const app = new Application({
546
- AppName,
547
- DisplayName: 'Bat App',
548
- });
549
- await app.Save(dbManager);
550
-
551
- const version = new Version({
552
- AppName,
553
- DefaultFile: 'someFile.html',
554
- IntegrationID: 'abcd',
555
- SemVer: '3.2.1-beta3',
556
- Status: 'deployed',
557
- Type: 'lambda-url',
558
- StartupType: 'direct',
559
- URL: 'https://abc123.lambda-url.us-east-1.on.aws',
560
- LambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:my-function',
561
- });
562
- await version.Save(dbManager);
563
-
564
- const rules = new Rules({
565
- AppName,
566
- Version: 0,
567
- RuleSet: { default: { SemVer: '3.2.1-beta3', AttributeName: '', AttributeValue: '' } },
568
- });
569
- await rules.Save(dbManager);
570
-
571
- // Call the handler
572
- const response = await GetRoute({
573
- dbManager,
574
- rawPath: `/_next/data/3.2.1-beta3/${AppName}/demo`,
575
- });
576
-
577
- expect(response).toBeDefined();
578
- expect(response).toHaveProperty('statusCode');
579
- expect(response.appName).toBe(AppName);
580
- expect(response.semVer).toBe('3.2.1-beta3');
581
- expect(response.statusCode).toBe(200);
582
- expect(response.url).toBe('https://abc123.lambda-url.us-east-1.on.aws');
583
- });
584
- });
585
- });
@@ -1,51 +0,0 @@
1
- import mockFs from 'mock-fs';
2
- import { loadAppFrame } from './load-app-frame';
3
- import Log from './lib/log';
4
-
5
- describe('loadAppFrame', () => {
6
- afterEach(() => {
7
- mockFs.restore();
8
- });
9
-
10
- it('should load appFrame.html from the default basePath', () => {
11
- const mockHtmlContent = '<html><body>App Frame</body></html>';
12
- mockFs({
13
- 'appFrame.html': mockHtmlContent,
14
- });
15
-
16
- expect(loadAppFrame({})).toBe(mockHtmlContent);
17
- });
18
-
19
- it('should load appFrame.html from a custom basePath', () => {
20
- const mockHtmlContent = '<html><body>App Frame</body></html>';
21
- const customBasePath = './custom';
22
- mockFs({
23
- [customBasePath]: {
24
- 'appFrame.html': mockHtmlContent,
25
- },
26
- });
27
-
28
- expect(loadAppFrame({ basePath: customBasePath })).toBe(mockHtmlContent);
29
- });
30
-
31
- it('should throw an error if appFrame.html is not found', () => {
32
- mockFs({});
33
-
34
- expect(() => {
35
- loadAppFrame({});
36
- }).toThrowError('appFrame.html not found');
37
- });
38
-
39
- it('should log the error if appFrame.html is not found', () => {
40
- mockFs({});
41
- const logSpy = jest.spyOn(Log.Instance, 'error');
42
-
43
- try {
44
- loadAppFrame({});
45
- } catch {
46
- // Catch the error to allow the test to continue
47
- }
48
-
49
- expect(logSpy).toHaveBeenCalledWith('appFrame.html not found');
50
- });
51
- });
@@ -1,27 +0,0 @@
1
- import { normalizePathPrefix } from './normalize-path-prefix';
2
-
3
- describe('normalizePathPrefix', () => {
4
- it('should return an empty string when the input is an empty string', () => {
5
- expect(normalizePathPrefix('')).toBe('');
6
- });
7
-
8
- it('should add a leading slash if it does not exist', () => {
9
- expect(normalizePathPrefix('path')).toBe('/path');
10
- });
11
-
12
- it('should not add a leading slash if it already exists', () => {
13
- expect(normalizePathPrefix('/path')).toBe('/path');
14
- });
15
-
16
- it('should remove a trailing slash if it exists', () => {
17
- expect(normalizePathPrefix('/path/')).toBe('/path');
18
- });
19
-
20
- it('should remove a trailing slash and add a leading slash if needed', () => {
21
- expect(normalizePathPrefix('path/')).toBe('/path');
22
- });
23
-
24
- it('should return the same input when it starts with a slash and does not end with a slash', () => {
25
- expect(normalizePathPrefix('/path/subpath')).toBe('/path/subpath');
26
- });
27
- });
@@ -1,98 +0,0 @@
1
- import { DBManager, Version } from '@pwrdrvr/microapps-datalib';
2
- import { AppVersionCache } from './app-cache';
3
- import { RedirectToDefaultFile } from './redirect-default-file';
4
-
5
- jest.mock('./app-cache');
6
-
7
- describe('RedirectToDefaultFile', () => {
8
- const mockDbManager = {} as DBManager;
9
-
10
- afterEach(() => {
11
- jest.clearAllMocks();
12
- });
13
-
14
- it('should return undefined if GetVersionInfo throws an error', async () => {
15
- (AppVersionCache.GetInstance as jest.Mock).mockImplementation(() => {
16
- return {
17
- GetVersionInfo: () => {
18
- throw new Error('GetVersionInfo error');
19
- },
20
- };
21
- });
22
-
23
- const result = await RedirectToDefaultFile({
24
- dbManager: mockDbManager,
25
- appName: 'testApp',
26
- semVer: '1.0.0',
27
- appNameOrRootTrailingSlash: '',
28
- });
29
-
30
- expect(result).toBeUndefined();
31
- });
32
-
33
- it('should return undefined if versionInfo is undefined', async () => {
34
- (AppVersionCache.GetInstance as jest.Mock).mockImplementation(() => {
35
- return {
36
- GetVersionInfo: () => undefined,
37
- };
38
- });
39
-
40
- const result = await RedirectToDefaultFile({
41
- dbManager: mockDbManager,
42
- appName: 'testApp',
43
- semVer: '1.0.0',
44
- appNameOrRootTrailingSlash: '',
45
- });
46
-
47
- expect(result).toBeUndefined();
48
- });
49
-
50
- it('should return undefined if versionInfo.DefaultFile is not set', async () => {
51
- const versionInfo: Version = {
52
- AppName: 'testApp',
53
- SemVer: '1.0.0',
54
- // @ts-expect-error this is a test after-all
55
- DefaultFile: undefined,
56
- };
57
- (AppVersionCache.GetInstance as jest.Mock).mockImplementation(() => {
58
- return {
59
- GetVersionInfo: () => versionInfo,
60
- };
61
- });
62
-
63
- const result = await RedirectToDefaultFile({
64
- dbManager: mockDbManager,
65
- appName: 'testApp',
66
- semVer: '1.0.0',
67
- appNameOrRootTrailingSlash: '',
68
- });
69
-
70
- expect(result).toBeUndefined();
71
- });
72
-
73
- it('should return a redirect with status code 302 when versionInfo.DefaultFile is set', async () => {
74
- // @ts-expect-error This is ok to be incomplete for the test
75
- const versionInfo: Version = {
76
- AppName: 'testApp',
77
- SemVer: '1.0.0',
78
- DefaultFile: 'index.html',
79
- };
80
- (AppVersionCache.GetInstance as jest.Mock).mockImplementation(() => {
81
- return {
82
- GetVersionInfo: () => versionInfo,
83
- };
84
- });
85
-
86
- const result = await RedirectToDefaultFile({
87
- dbManager: mockDbManager,
88
- appName: 'testApp',
89
- semVer: '1.0.0',
90
- appNameOrRootTrailingSlash: 'testapp/',
91
- });
92
-
93
- expect(result).toEqual({
94
- statusCode: 302,
95
- redirectLocation: '/testapp/1.0.0/index.html',
96
- });
97
- });
98
- });
@@ -1,128 +0,0 @@
1
- import { DBManager } from '@pwrdrvr/microapps-datalib';
2
- import { AppVersionCache } from './app-cache';
3
- import { RouteApp } from './route-app';
4
-
5
- jest.mock('./app-cache');
6
-
7
- describe('RouteApp', () => {
8
- const mockDbManager = {} as DBManager;
9
-
10
- afterEach(() => {
11
- jest.clearAllMocks();
12
- });
13
-
14
- const testCases = [
15
- {
16
- caseName: 'should route simple app with iframe startup and no versions specified',
17
- possibleSemVerPathAfterAppVersionInfo: undefined,
18
- possibleSemVerPathNextDataVersionInfo: undefined,
19
- possibleSemVerQueryVersionInfo: undefined,
20
- possibleSemVerQuery: undefined,
21
- defaultVersion: '1.0.0',
22
- expectedStatusCode: 200,
23
- expectedAppName: 'testApp',
24
- expectedSemVer: '1.0.0',
25
- expectedStartupType: 'iframe',
26
- },
27
- {
28
- caseName: 'uses the path specified version instead of the default version',
29
- possibleSemVerPathAfterAppVersionInfo: {
30
- SemVer: '1.0.1',
31
- DefaultFile: 'index.html',
32
- StartupType: 'iframe',
33
- },
34
- possibleSemVerPathNextDataVersionInfo: undefined,
35
- possibleSemVerQueryVersionInfo: undefined,
36
- possibleSemVerQuery: undefined,
37
- defaultVersion: '1.0.0',
38
- expectedStatusCode: 200,
39
- expectedAppName: 'testApp',
40
- expectedSemVer: '1.0.1',
41
- expectedStartupType: undefined,
42
- },
43
- {
44
- caseName: 'uses the query string specified version instead of the default version',
45
- possibleSemVerPathAfterAppVersionInfo: undefined,
46
- possibleSemVerPathNextDataVersionInfo: undefined,
47
- possibleSemVerQueryVersionInfo: {
48
- SemVer: '1.0.3',
49
- DefaultFile: 'index.html',
50
- StartupType: 'iframe',
51
- },
52
- possibleSemVerQuery: '1.0.3',
53
- defaultVersion: '1.0.0',
54
- expectedStatusCode: 200,
55
- expectedAppName: 'testApp',
56
- expectedSemVer: '1.0.3',
57
- expectedStartupType: 'iframe',
58
- },
59
- {
60
- caseName: 'gives a 404 when the query string version does not exist',
61
- possibleSemVerPathAfterAppVersionInfo: undefined,
62
- possibleSemVerPathNextDataVersionInfo: undefined,
63
- possibleSemVerQueryVersionInfo: undefined,
64
- possibleSemVerQuery: '1.0.4',
65
- defaultVersion: '1.0.0',
66
- expectedStatusCode: 404,
67
- expectedAppName: undefined,
68
- expectedSemVer: undefined,
69
- expectedStartupType: undefined,
70
- },
71
- ];
72
-
73
- it.each(testCases)(
74
- '$caseName',
75
- async ({
76
- possibleSemVerPathAfterAppVersionInfo,
77
- possibleSemVerPathNextDataVersionInfo,
78
- possibleSemVerQueryVersionInfo,
79
- possibleSemVerQuery,
80
- defaultVersion,
81
- expectedStatusCode,
82
- expectedAppName,
83
- expectedSemVer,
84
- expectedStartupType,
85
- }) => {
86
- (AppVersionCache.GetInstance as jest.Mock).mockImplementation(() => {
87
- return {
88
- GetVersionInfo: (options: { key: { AppName: string; SemVer: string } }) => {
89
- if (options.key.SemVer === 'pathAfterApp') {
90
- return possibleSemVerPathAfterAppVersionInfo;
91
- }
92
- if (options.key.SemVer === 'pathNextData') {
93
- return possibleSemVerPathNextDataVersionInfo;
94
- }
95
- if (options.key.SemVer === 'query') {
96
- return possibleSemVerQueryVersionInfo;
97
- }
98
- if (options.key.SemVer === defaultVersion) {
99
- return { ...options.key, DefaultFile: 'index.html', StartupType: 'iframe' };
100
- }
101
- return undefined;
102
- },
103
- GetRules: () => ({ RuleSet: { default: { SemVer: defaultVersion } } }),
104
- };
105
- });
106
-
107
- const result = await RouteApp({
108
- dbManager: mockDbManager,
109
- event: { dbManager: mockDbManager, locales: [], rawPath: '/testApp' },
110
- appName: 'testApp',
111
- possibleSemVerPathNextData: possibleSemVerPathNextDataVersionInfo
112
- ? 'pathNextData'
113
- : undefined,
114
- possibleSemVerPathAfterApp: possibleSemVerPathAfterAppVersionInfo
115
- ? 'pathAfterApp'
116
- : undefined,
117
- possibleSemVerQuery: possibleSemVerQueryVersionInfo ? 'query' : possibleSemVerQuery,
118
- additionalParts: '',
119
- appNameOrRootTrailingSlash: '',
120
- });
121
-
122
- expect(result.statusCode).toBe(expectedStatusCode);
123
- expect(result.appName).toBe(expectedAppName);
124
- expect(result.semVer).toBe(expectedSemVer);
125
- expect(result.startupType).toBe(expectedStartupType);
126
- },
127
- );
128
- });