@friggframework/devtools 2.0.0--canary.398.53eac55.0 → 2.0.0--canary.397.878fefa.0

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.
@@ -1,498 +0,0 @@
1
- const { composeServerlessDefinition } = require('./serverless-template');
2
-
3
- // Mock the node_modules path finding functions
4
- jest.mock('./serverless-template', () => {
5
- const originalModule = jest.requireActual('./serverless-template');
6
- return {
7
- ...originalModule,
8
- composeServerlessDefinition: originalModule.composeServerlessDefinition
9
- };
10
- });
11
-
12
- describe('composeServerlessDefinition', () => {
13
- let mockIntegration;
14
-
15
- beforeEach(() => {
16
- mockIntegration = {
17
- Definition: {
18
- name: 'testIntegration'
19
- }
20
- };
21
-
22
- // Mock process.argv to avoid offline mode during tests
23
- jest.spyOn(process, 'argv', 'get').mockReturnValue(['node', 'test']);
24
- });
25
-
26
- afterEach(() => {
27
- jest.restoreAllMocks();
28
- });
29
-
30
- describe('Basic Configuration', () => {
31
- it('should create basic serverless definition with minimal app definition', () => {
32
- const appDefinition = {
33
- name: 'test-app',
34
- integrations: []
35
- };
36
-
37
- const result = composeServerlessDefinition(appDefinition);
38
-
39
- expect(result.service).toBe('test-app');
40
- expect(result.provider.name).toBe('aws');
41
- expect(result.provider.runtime).toBe('nodejs20.x');
42
- expect(result.provider.region).toBe('us-east-1');
43
- expect(result.provider.stage).toBe('${opt:stage}');
44
- expect(result.frameworkVersion).toBe('>=3.17.0');
45
- });
46
-
47
- it('should use default service name when name not provided', () => {
48
- const appDefinition = {
49
- integrations: []
50
- };
51
-
52
- const result = composeServerlessDefinition(appDefinition);
53
-
54
- expect(result.service).toBe('create-frigg-app');
55
- });
56
-
57
- it('should use custom provider when specified', () => {
58
- const appDefinition = {
59
- provider: 'custom-provider',
60
- integrations: []
61
- };
62
-
63
- const result = composeServerlessDefinition(appDefinition);
64
-
65
- expect(result.provider.name).toBe('custom-provider');
66
- });
67
- });
68
-
69
- describe('VPC Configuration', () => {
70
- it('should add VPC configuration when vpc.enable is true', () => {
71
- const appDefinition = {
72
- vpc: { enable: true },
73
- integrations: []
74
- };
75
-
76
- const result = composeServerlessDefinition(appDefinition);
77
-
78
- expect(result.provider.vpc).toBe('${self:custom.vpc.${self:provider.stage}}');
79
- expect(result.custom.vpc).toEqual({
80
- '${self:provider.stage}': {
81
- securityGroupIds: ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
82
- subnetIds: [
83
- '${env:AWS_DISCOVERY_SUBNET_ID_1}',
84
- '${env:AWS_DISCOVERY_SUBNET_ID_2}'
85
- ]
86
- }
87
- });
88
- });
89
-
90
- it('should add VPC endpoint for S3 when VPC is enabled', () => {
91
- const appDefinition = {
92
- vpc: { enable: true },
93
- integrations: []
94
- };
95
-
96
- const result = composeServerlessDefinition(appDefinition);
97
-
98
- expect(result.resources.Resources.VPCEndpointS3).toEqual({
99
- Type: 'AWS::EC2::VPCEndpoint',
100
- Properties: {
101
- VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
102
- ServiceName: 'com.amazonaws.${self:provider.region}.s3',
103
- VpcEndpointType: 'Gateway',
104
- RouteTableIds: ['${env:AWS_DISCOVERY_ROUTE_TABLE_ID}']
105
- }
106
- });
107
- });
108
-
109
- it('should not add VPC configuration when vpc.enable is false', () => {
110
- const appDefinition = {
111
- vpc: { enable: false },
112
- integrations: []
113
- };
114
-
115
- const result = composeServerlessDefinition(appDefinition);
116
-
117
- expect(result.provider.vpc).toBeUndefined();
118
- expect(result.custom.vpc).toBeUndefined();
119
- expect(result.resources.Resources.VPCEndpointS3).toBeUndefined();
120
- });
121
-
122
- it('should not add VPC configuration when vpc is not defined', () => {
123
- const appDefinition = {
124
- integrations: []
125
- };
126
-
127
- const result = composeServerlessDefinition(appDefinition);
128
-
129
- expect(result.provider.vpc).toBeUndefined();
130
- expect(result.custom.vpc).toBeUndefined();
131
- });
132
- });
133
-
134
- describe('KMS Configuration', () => {
135
- it('should add KMS configuration when encryption is enabled', () => {
136
- const appDefinition = {
137
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
138
- integrations: []
139
- };
140
-
141
- const result = composeServerlessDefinition(appDefinition);
142
-
143
- // Check IAM permissions
144
- const kmsPermission = result.provider.iamRoleStatements.find(
145
- statement => statement.Action.includes('kms:GenerateDataKey')
146
- );
147
- expect(kmsPermission).toEqual({
148
- Effect: 'Allow',
149
- Action: [
150
- 'kms:GenerateDataKey',
151
- 'kms:Decrypt'
152
- ],
153
- Resource: ['${self:custom.kmsGrants.kmsKeyId}']
154
- });
155
-
156
- // Check environment variable
157
- expect(result.provider.environment.KMS_KEY_ARN).toBe('${self:custom.kmsGrants.kmsKeyId}');
158
-
159
- // Check plugin
160
- expect(result.plugins).toContain('serverless-kms-grants');
161
-
162
- // Check custom configuration
163
- expect(result.custom.kmsGrants).toEqual({
164
- kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
165
- });
166
- });
167
-
168
- it('should not add KMS configuration when encryption is disabled', () => {
169
- const appDefinition = {
170
- encryption: { useDefaultKMSForFieldLevelEncryption: false },
171
- integrations: []
172
- };
173
-
174
- const result = composeServerlessDefinition(appDefinition);
175
-
176
- const kmsPermission = result.provider.iamRoleStatements.find(
177
- statement => statement.Action && statement.Action.includes('kms:GenerateDataKey')
178
- );
179
- expect(kmsPermission).toBeUndefined();
180
- expect(result.provider.environment.KMS_KEY_ARN).toBeUndefined();
181
- expect(result.plugins).not.toContain('serverless-kms-grants');
182
- expect(result.custom.kmsGrants).toBeUndefined();
183
- });
184
-
185
- it('should not add KMS configuration when encryption is not defined', () => {
186
- const appDefinition = {
187
- integrations: []
188
- };
189
-
190
- const result = composeServerlessDefinition(appDefinition);
191
-
192
- const kmsPermission = result.provider.iamRoleStatements.find(
193
- statement => statement.Action && statement.Action.includes('kms:GenerateDataKey')
194
- );
195
- expect(kmsPermission).toBeUndefined();
196
- expect(result.custom.kmsGrants).toBeUndefined();
197
- });
198
- });
199
-
200
- describe('SSM Configuration', () => {
201
- it('should add SSM configuration when ssm.enable is true', () => {
202
- const appDefinition = {
203
- ssm: { enable: true },
204
- integrations: []
205
- };
206
-
207
- const result = composeServerlessDefinition(appDefinition);
208
-
209
- // Check lambda layers
210
- expect(result.provider.layers).toEqual([
211
- 'arn:aws:lambda:${self:provider.region}:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11'
212
- ]);
213
-
214
- // Check IAM permissions
215
- const ssmPermission = result.provider.iamRoleStatements.find(
216
- statement => statement.Action.includes('ssm:GetParameter')
217
- );
218
- expect(ssmPermission).toEqual({
219
- Effect: 'Allow',
220
- Action: [
221
- 'ssm:GetParameter',
222
- 'ssm:GetParameters',
223
- 'ssm:GetParametersByPath'
224
- ],
225
- Resource: [
226
- 'arn:aws:ssm:${self:provider.region}:*:parameter/${self:service}/${self:provider.stage}/*'
227
- ]
228
- });
229
-
230
- // Check environment variable
231
- expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBe('/${self:service}/${self:provider.stage}');
232
- });
233
-
234
- it('should not add SSM configuration when ssm.enable is false', () => {
235
- const appDefinition = {
236
- ssm: { enable: false },
237
- integrations: []
238
- };
239
-
240
- const result = composeServerlessDefinition(appDefinition);
241
-
242
- expect(result.provider.layers).toBeUndefined();
243
-
244
- const ssmPermission = result.provider.iamRoleStatements.find(
245
- statement => statement.Action && statement.Action.includes('ssm:GetParameter')
246
- );
247
- expect(ssmPermission).toBeUndefined();
248
- expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
249
- });
250
-
251
- it('should not add SSM configuration when ssm is not defined', () => {
252
- const appDefinition = {
253
- integrations: []
254
- };
255
-
256
- const result = composeServerlessDefinition(appDefinition);
257
-
258
- expect(result.provider.layers).toBeUndefined();
259
- expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
260
- });
261
- });
262
-
263
- describe('Integration Configuration', () => {
264
- it('should add integration-specific resources and functions', () => {
265
- const appDefinition = {
266
- integrations: [mockIntegration]
267
- };
268
-
269
- const result = composeServerlessDefinition(appDefinition);
270
-
271
- // Check integration function
272
- expect(result.functions.testIntegration).toEqual({
273
- handler: 'node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.testIntegration.handler',
274
- events: [{
275
- http: {
276
- path: '/api/testIntegration-integration/{proxy+}',
277
- method: 'ANY',
278
- cors: true
279
- }
280
- }]
281
- });
282
-
283
- // Check SQS Queue
284
- expect(result.resources.Resources.TestIntegrationQueue).toEqual({
285
- Type: 'AWS::SQS::Queue',
286
- Properties: {
287
- QueueName: '${self:custom.TestIntegrationQueue}',
288
- MessageRetentionPeriod: 60,
289
- VisibilityTimeout: 1800,
290
- RedrivePolicy: {
291
- maxReceiveCount: 1,
292
- deadLetterTargetArn: {
293
- 'Fn::GetAtt': ['InternalErrorQueue', 'Arn']
294
- }
295
- }
296
- }
297
- });
298
-
299
- // Check Queue Worker
300
- expect(result.functions.testIntegrationQueueWorker).toEqual({
301
- handler: 'node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.testIntegration.queueWorker',
302
- reservedConcurrency: 5,
303
- events: [{
304
- sqs: {
305
- arn: {
306
- 'Fn::GetAtt': ['TestIntegrationQueue', 'Arn']
307
- },
308
- batchSize: 1
309
- }
310
- }],
311
- timeout: 600
312
- });
313
-
314
- // Check environment variable
315
- expect(result.provider.environment.TESTINTEGRATION_QUEUE_URL).toEqual({
316
- Ref: 'TestIntegrationQueue'
317
- });
318
-
319
- // Check custom queue name
320
- expect(result.custom.TestIntegrationQueue).toBe('${self:service}--${self:provider.stage}-TestIntegrationQueue');
321
- });
322
-
323
- it('should handle multiple integrations', () => {
324
- const secondIntegration = {
325
- Definition: {
326
- name: 'secondIntegration'
327
- }
328
- };
329
-
330
- const appDefinition = {
331
- integrations: [mockIntegration, secondIntegration]
332
- };
333
-
334
- const result = composeServerlessDefinition(appDefinition);
335
-
336
- expect(result.functions.testIntegration).toBeDefined();
337
- expect(result.functions.secondIntegration).toBeDefined();
338
- expect(result.functions.testIntegrationQueueWorker).toBeDefined();
339
- expect(result.functions.secondIntegrationQueueWorker).toBeDefined();
340
- expect(result.resources.Resources.TestIntegrationQueue).toBeDefined();
341
- expect(result.resources.Resources.SecondIntegrationQueue).toBeDefined();
342
- });
343
- });
344
-
345
- describe('Combined Configurations', () => {
346
- it('should combine VPC, KMS, and SSM configurations', () => {
347
- const appDefinition = {
348
- vpc: { enable: true },
349
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
350
- ssm: { enable: true },
351
- integrations: [mockIntegration]
352
- };
353
-
354
- const result = composeServerlessDefinition(appDefinition);
355
-
356
- // VPC
357
- expect(result.provider.vpc).toBeDefined();
358
- expect(result.custom.vpc).toBeDefined();
359
- expect(result.resources.Resources.VPCEndpointS3).toBeDefined();
360
-
361
- // KMS
362
- expect(result.plugins).toContain('serverless-kms-grants');
363
- expect(result.provider.environment.KMS_KEY_ARN).toBeDefined();
364
- expect(result.custom.kmsGrants).toBeDefined();
365
-
366
- // SSM
367
- expect(result.provider.layers).toBeDefined();
368
- expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeDefined();
369
-
370
- // Integration
371
- expect(result.functions.testIntegration).toBeDefined();
372
- expect(result.resources.Resources.TestIntegrationQueue).toBeDefined();
373
-
374
- // All plugins should be present
375
- expect(result.plugins).toEqual([
376
- 'serverless-jetpack',
377
- 'serverless-dotenv-plugin',
378
- 'serverless-offline-sqs',
379
- 'serverless-offline',
380
- '@friggframework/serverless-plugin',
381
- 'serverless-kms-grants'
382
- ]);
383
- });
384
-
385
- it('should handle partial configuration combinations', () => {
386
- const appDefinition = {
387
- vpc: { enable: true },
388
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
389
- integrations: []
390
- };
391
-
392
- const result = composeServerlessDefinition(appDefinition);
393
-
394
- // VPC and KMS should be present
395
- expect(result.provider.vpc).toBeDefined();
396
- expect(result.custom.kmsGrants).toBeDefined();
397
-
398
- // SSM should not be present
399
- expect(result.provider.layers).toBeUndefined();
400
- expect(result.provider.environment.SSM_PARAMETER_PREFIX).toBeUndefined();
401
- });
402
- });
403
-
404
- describe('Default Resources', () => {
405
- it('should always include default resources', () => {
406
- const appDefinition = {
407
- integrations: []
408
- };
409
-
410
- const result = composeServerlessDefinition(appDefinition);
411
-
412
- // Check default resources are always present
413
- expect(result.resources.Resources.InternalErrorQueue).toBeDefined();
414
- expect(result.resources.Resources.InternalErrorBridgeTopic).toBeDefined();
415
- expect(result.resources.Resources.InternalErrorBridgePolicy).toBeDefined();
416
- expect(result.resources.Resources.ApiGatewayAlarm5xx).toBeDefined();
417
-
418
- // Check default functions
419
- expect(result.functions.defaultWebsocket).toBeDefined();
420
- expect(result.functions.auth).toBeDefined();
421
- expect(result.functions.user).toBeDefined();
422
-
423
- // Check default plugins
424
- expect(result.plugins).toContain('serverless-jetpack');
425
- expect(result.plugins).toContain('serverless-dotenv-plugin');
426
- expect(result.plugins).toContain('@friggframework/serverless-plugin');
427
- });
428
-
429
- it('should always include default IAM permissions', () => {
430
- const appDefinition = {
431
- integrations: []
432
- };
433
-
434
- const result = composeServerlessDefinition(appDefinition);
435
-
436
- // Check SNS publish permission
437
- const snsPermission = result.provider.iamRoleStatements.find(
438
- statement => statement.Action.includes('sns:Publish')
439
- );
440
- expect(snsPermission).toBeDefined();
441
-
442
- // Check SQS permissions
443
- const sqsPermission = result.provider.iamRoleStatements.find(
444
- statement => statement.Action.includes('sqs:SendMessage')
445
- );
446
- expect(sqsPermission).toBeDefined();
447
- });
448
-
449
- it('should include default environment variables', () => {
450
- const appDefinition = {
451
- integrations: []
452
- };
453
-
454
- const result = composeServerlessDefinition(appDefinition);
455
-
456
- expect(result.provider.environment.STAGE).toBe('${opt:stage}');
457
- expect(result.provider.environment.AWS_NODEJS_CONNECTION_REUSE_ENABLED).toBe(1);
458
- });
459
- });
460
-
461
- describe('Edge Cases', () => {
462
- it('should handle empty app definition', () => {
463
- const appDefinition = {};
464
-
465
- expect(() => composeServerlessDefinition(appDefinition)).not.toThrow();
466
- const result = composeServerlessDefinition(appDefinition);
467
- expect(result.service).toBe('create-frigg-app');
468
- });
469
-
470
- it('should handle null/undefined integrations', () => {
471
- const appDefinition = {
472
- integrations: null
473
- };
474
-
475
- expect(() => composeServerlessDefinition(appDefinition)).toThrow();
476
- });
477
-
478
- it('should handle integration with missing Definition', () => {
479
- const invalidIntegration = {};
480
- const appDefinition = {
481
- integrations: [invalidIntegration]
482
- };
483
-
484
- expect(() => composeServerlessDefinition(appDefinition)).toThrow();
485
- });
486
-
487
- it('should handle integration with missing name', () => {
488
- const invalidIntegration = {
489
- Definition: {}
490
- };
491
- const appDefinition = {
492
- integrations: [invalidIntegration]
493
- };
494
-
495
- expect(() => composeServerlessDefinition(appDefinition)).toThrow();
496
- });
497
- });
498
- });
@@ -1,125 +0,0 @@
1
- const {
2
- Auther,
3
- ModuleConstants,
4
- createObjectId,
5
- connectToDatabase,
6
- disconnectFromDatabase,
7
- } = require('@friggframework/core');
8
- const { createMockApiObject } = require("./mock-integration");
9
-
10
-
11
- function testAutherDefinition(definition, mocks) {
12
- const getModule = async (params) => {
13
- const module = await Auther.getInstance({
14
- definition,
15
- userId: createObjectId(),
16
- ...params,
17
- });
18
- if (mocks.tokenResponse) {
19
- mocks.getTokenFrom = async function(code) {
20
- await this.setTokens(mocks.tokenResponse);
21
- return mocks.tokenResponse
22
- }
23
- mocks.getTokenFromCode = mocks.getTokenFromCode || mocks.getTokenFrom
24
- mocks.getTokenFromCodeBasicAuthHeader = mocks.getTokenFromCodeBasicAuthHeader || mocks.getTokenFrom
25
- mocks.getTokenFromClientCredentials = mocks.getTokenFromClientCredentials || mocks.getTokenFrom
26
- mocks.getTokenFromUsernamePassword = mocks.getTokenFromUsernamePassword || mocks.getTokenFrom
27
- }
28
- if (mocks.refreshResponse) {
29
- mocks.refreshAccessToken = async function(code) {
30
- await this.setTokens(mocks.refreshResponse);
31
- return mocks.refreshResponse
32
- }
33
- }
34
- module.api = createMockApiObject(jest, module.api, mocks);
35
- return module
36
- }
37
-
38
-
39
- describe(`${definition.moduleName} Module Tests`, () => {
40
- let module, authUrl;
41
- beforeAll(async () => {
42
- await connectToDatabase();
43
- module = await getModule();
44
- });
45
-
46
- afterAll(async () => {
47
- await disconnectFromDatabase();
48
- });
49
-
50
- let requirements, authCallbackParams;
51
- if (definition.API.requesterType === ModuleConstants.authType.oauth2) {
52
- authCallbackParams = mocks.authorizeResponse || mocks.authorizeParams;
53
- describe('getAuthorizationRequirements() test', () => {
54
- it('should return auth requirements', async () => {
55
- requirements = await module.getAuthorizationRequirements();
56
- expect(requirements).toBeDefined();
57
- expect(requirements.type).toEqual(ModuleConstants.authType.oauth2);
58
- expect(requirements.url).toBeDefined();
59
- authUrl = requirements.url;
60
- });
61
- });
62
- } else if (definition.API.requesterType === ModuleConstants.authType.basic) {
63
- // could also confirm authCallbackParams against the auth requirements
64
- authCallbackParams = mocks.authorizeParams
65
- describe('getAuthorizationRequirements() test', () => {
66
- it('should return auth requirements', async () => {
67
- requirements = module.getAuthorizationRequirements();
68
- expect(requirements).toBeDefined();
69
- expect(requirements.type).toEqual(ModuleConstants.authType.basic);
70
- });
71
- });
72
- } else if (definition.API.requesterType === ModuleConstants.authType.apiKey) {
73
- // could also confirm authCallbackParams against the auth requirements
74
- authCallbackParams = mocks.authorizeParams
75
- describe('getAuthorizationRequirements() test', () => {
76
- it('should return auth requirements', async () => {
77
- requirements = module.getAuthorizationRequirements();
78
- expect(requirements).toBeDefined();
79
- expect(requirements.type).toEqual(ModuleConstants.authType.apiKey);
80
- });
81
- });
82
- }
83
-
84
- describe('Authorization requests', () => {
85
- let firstRes;
86
- it('processAuthorizationCallback()', async () => {
87
- firstRes = await module.processAuthorizationCallback(authCallbackParams);
88
- expect(firstRes).toBeDefined();
89
- expect(firstRes.entity_id).toBeDefined();
90
- expect(firstRes.credential_id).toBeDefined();
91
- });
92
- it('retrieves existing entity on subsequent calls', async () => {
93
- const res = await module.processAuthorizationCallback(authCallbackParams);
94
- expect(res).toEqual(firstRes);
95
- });
96
- });
97
-
98
- describe('Test credential retrieval and module instantiation', () => {
99
- it('retrieve by entity id', async () => {
100
- const newModule = await getModule({
101
- userId: module.userId,
102
- entityId: module.entity.id
103
- });
104
- expect(newModule).toBeDefined();
105
- expect(newModule.entity).toBeDefined();
106
- expect(newModule.credential).toBeDefined();
107
- expect(await newModule.testAuth()).toBeTruthy();
108
-
109
- });
110
-
111
- it('retrieve by credential id', async () => {
112
- const newModule = await getModule({
113
- userId: module.userId,
114
- credentialId: module.credential.id
115
- });
116
- expect(newModule).toBeDefined();
117
- expect(newModule.credential).toBeDefined();
118
- expect(await newModule.testAuth()).toBeTruthy();
119
- });
120
- });
121
- });
122
- }
123
-
124
- module.exports = { testAutherDefinition }
125
-