@friggframework/devtools 2.0.0--canary.397.fe6d7a2.0 → 2.0.0--canary.405.45825cd.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,6 +1,7 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const { composeServerlessDefinition } = require('./serverless-template');
4
+
4
5
  const { findNearestBackendPackageJson } = require('@friggframework/core');
5
6
 
6
7
  function createFriggInfrastructure() {
@@ -18,7 +19,14 @@ function createFriggInfrastructure() {
18
19
  const backend = require(backendFilePath);
19
20
  const appDefinition = backend.Definition;
20
21
 
21
- const definition = composeServerlessDefinition(appDefinition);
22
+ // const serverlessTemplate = require(path.resolve(
23
+ // __dirname,
24
+ // './serverless-template.js'
25
+ // ));
26
+ const definition = composeServerlessDefinition(
27
+ appDefinition,
28
+ backend.IntegrationFactory
29
+ );
22
30
 
23
31
  return {
24
32
  ...definition,
@@ -102,7 +102,325 @@ const modifyHandlerPaths = (functions) => {
102
102
  return modifiedFunctions;
103
103
  };
104
104
 
105
+ // Helper function to create VPC infrastructure resources
106
+ const createVPCInfrastructure = (AppDefinition) => {
107
+ const vpcResources = {
108
+ // VPC
109
+ FriggVPC: {
110
+ Type: 'AWS::EC2::VPC',
111
+ Properties: {
112
+ CidrBlock: AppDefinition.vpc.cidrBlock || '10.0.0.0/16',
113
+ EnableDnsHostnames: true,
114
+ EnableDnsSupport: true,
115
+ Tags: [
116
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc' }
117
+ ]
118
+ }
119
+ },
120
+
121
+ // Internet Gateway
122
+ FriggInternetGateway: {
123
+ Type: 'AWS::EC2::InternetGateway',
124
+ Properties: {
125
+ Tags: [
126
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-igw' }
127
+ ]
128
+ }
129
+ },
130
+
131
+ // Attach Internet Gateway to VPC
132
+ FriggVPCGatewayAttachment: {
133
+ Type: 'AWS::EC2::VPCGatewayAttachment',
134
+ Properties: {
135
+ VpcId: { Ref: 'FriggVPC' },
136
+ InternetGatewayId: { Ref: 'FriggInternetGateway' }
137
+ }
138
+ },
139
+
140
+ // Public Subnet for NAT Gateway
141
+ FriggPublicSubnet: {
142
+ Type: 'AWS::EC2::Subnet',
143
+ Properties: {
144
+ VpcId: { Ref: 'FriggVPC' },
145
+ CidrBlock: '10.0.1.0/24',
146
+ AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
147
+ MapPublicIpOnLaunch: true,
148
+ Tags: [
149
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-subnet' }
150
+ ]
151
+ }
152
+ },
153
+
154
+ // Private Subnet 1 for Lambda
155
+ FriggPrivateSubnet1: {
156
+ Type: 'AWS::EC2::Subnet',
157
+ Properties: {
158
+ VpcId: { Ref: 'FriggVPC' },
159
+ CidrBlock: '10.0.2.0/24',
160
+ AvailabilityZone: { 'Fn::Select': [0, { 'Fn::GetAZs': '' }] },
161
+ Tags: [
162
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-1' }
163
+ ]
164
+ }
165
+ },
166
+
167
+ // Private Subnet 2 for Lambda (different AZ for redundancy)
168
+ FriggPrivateSubnet2: {
169
+ Type: 'AWS::EC2::Subnet',
170
+ Properties: {
171
+ VpcId: { Ref: 'FriggVPC' },
172
+ CidrBlock: '10.0.3.0/24',
173
+ AvailabilityZone: { 'Fn::Select': [1, { 'Fn::GetAZs': '' }] },
174
+ Tags: [
175
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-subnet-2' }
176
+ ]
177
+ }
178
+ },
179
+
180
+ // Elastic IP for NAT Gateway
181
+ FriggNATGatewayEIP: {
182
+ Type: 'AWS::EC2::EIP',
183
+ Properties: {
184
+ Domain: 'vpc',
185
+ Tags: [
186
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-eip' }
187
+ ]
188
+ },
189
+ DependsOn: 'FriggVPCGatewayAttachment'
190
+ },
191
+
192
+ // NAT Gateway for private subnet internet access
193
+ FriggNATGateway: {
194
+ Type: 'AWS::EC2::NatGateway',
195
+ Properties: {
196
+ AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
197
+ SubnetId: { Ref: 'FriggPublicSubnet' },
198
+ Tags: [
199
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' }
200
+ ]
201
+ }
202
+ },
203
+
204
+ // Public Route Table
205
+ FriggPublicRouteTable: {
206
+ Type: 'AWS::EC2::RouteTable',
207
+ Properties: {
208
+ VpcId: { Ref: 'FriggVPC' },
209
+ Tags: [
210
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-public-rt' }
211
+ ]
212
+ }
213
+ },
214
+
215
+ // Public Route to Internet Gateway
216
+ FriggPublicRoute: {
217
+ Type: 'AWS::EC2::Route',
218
+ Properties: {
219
+ RouteTableId: { Ref: 'FriggPublicRouteTable' },
220
+ DestinationCidrBlock: '0.0.0.0/0',
221
+ GatewayId: { Ref: 'FriggInternetGateway' }
222
+ },
223
+ DependsOn: 'FriggVPCGatewayAttachment'
224
+ },
225
+
226
+ // Associate Public Subnet with Public Route Table
227
+ FriggPublicSubnetRouteTableAssociation: {
228
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
229
+ Properties: {
230
+ SubnetId: { Ref: 'FriggPublicSubnet' },
231
+ RouteTableId: { Ref: 'FriggPublicRouteTable' }
232
+ }
233
+ },
234
+
235
+ // Private Route Table
236
+ FriggPrivateRouteTable: {
237
+ Type: 'AWS::EC2::RouteTable',
238
+ Properties: {
239
+ VpcId: { Ref: 'FriggVPC' },
240
+ Tags: [
241
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-private-rt' }
242
+ ]
243
+ }
244
+ },
245
+
246
+ // Private Route to NAT Gateway
247
+ FriggPrivateRoute: {
248
+ Type: 'AWS::EC2::Route',
249
+ Properties: {
250
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' },
251
+ DestinationCidrBlock: '0.0.0.0/0',
252
+ NatGatewayId: { Ref: 'FriggNATGateway' }
253
+ }
254
+ },
255
+
256
+ // Associate Private Subnet 1 with Private Route Table
257
+ FriggPrivateSubnet1RouteTableAssociation: {
258
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
259
+ Properties: {
260
+ SubnetId: { Ref: 'FriggPrivateSubnet1' },
261
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' }
262
+ }
263
+ },
264
+
265
+ // Associate Private Subnet 2 with Private Route Table
266
+ FriggPrivateSubnet2RouteTableAssociation: {
267
+ Type: 'AWS::EC2::SubnetRouteTableAssociation',
268
+ Properties: {
269
+ SubnetId: { Ref: 'FriggPrivateSubnet2' },
270
+ RouteTableId: { Ref: 'FriggPrivateRouteTable' }
271
+ }
272
+ },
273
+
274
+ // Security Group for Lambda functions
275
+ FriggLambdaSecurityGroup: {
276
+ Type: 'AWS::EC2::SecurityGroup',
277
+ Properties: {
278
+ GroupDescription: 'Security group for Frigg Lambda functions',
279
+ VpcId: { Ref: 'FriggVPC' },
280
+ SecurityGroupEgress: [
281
+ {
282
+ IpProtocol: 'tcp',
283
+ FromPort: 443,
284
+ ToPort: 443,
285
+ CidrIp: '0.0.0.0/0',
286
+ Description: 'HTTPS outbound'
287
+ },
288
+ {
289
+ IpProtocol: 'tcp',
290
+ FromPort: 80,
291
+ ToPort: 80,
292
+ CidrIp: '0.0.0.0/0',
293
+ Description: 'HTTP outbound'
294
+ },
295
+ {
296
+ IpProtocol: 'tcp',
297
+ FromPort: 27017,
298
+ ToPort: 27017,
299
+ CidrIp: '0.0.0.0/0',
300
+ Description: 'MongoDB Atlas TLS outbound'
301
+ },
302
+ {
303
+ IpProtocol: 'tcp',
304
+ FromPort: 53,
305
+ ToPort: 53,
306
+ CidrIp: '0.0.0.0/0',
307
+ Description: 'DNS TCP'
308
+ },
309
+ {
310
+ IpProtocol: 'udp',
311
+ FromPort: 53,
312
+ ToPort: 53,
313
+ CidrIp: '0.0.0.0/0',
314
+ Description: 'DNS UDP'
315
+ }
316
+ ],
317
+ Tags: [
318
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-sg' }
319
+ ]
320
+ }
321
+ }
322
+ };
323
+
324
+ // Add VPC Endpoints for cost optimization
325
+ if (AppDefinition.vpc.enableVPCEndpoints !== false) {
326
+ // S3 Gateway Endpoint (free)
327
+ vpcResources.FriggS3VPCEndpoint = {
328
+ Type: 'AWS::EC2::VPCEndpoint',
329
+ Properties: {
330
+ VpcId: { Ref: 'FriggVPC' },
331
+ ServiceName: 'com.amazonaws.${self:provider.region}.s3',
332
+ VpcEndpointType: 'Gateway',
333
+ RouteTableIds: [
334
+ { Ref: 'FriggPrivateRouteTable' }
335
+ ]
336
+ }
337
+ };
338
+
339
+ // DynamoDB Gateway Endpoint (free)
340
+ vpcResources.FriggDynamoDBVPCEndpoint = {
341
+ Type: 'AWS::EC2::VPCEndpoint',
342
+ Properties: {
343
+ VpcId: { Ref: 'FriggVPC' },
344
+ ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
345
+ VpcEndpointType: 'Gateway',
346
+ RouteTableIds: [
347
+ { Ref: 'FriggPrivateRouteTable' }
348
+ ]
349
+ }
350
+ };
351
+
352
+ // KMS Interface Endpoint (paid, but useful if using KMS)
353
+ if (AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true) {
354
+ vpcResources.FriggKMSVPCEndpoint = {
355
+ Type: 'AWS::EC2::VPCEndpoint',
356
+ Properties: {
357
+ VpcId: { Ref: 'FriggVPC' },
358
+ ServiceName: 'com.amazonaws.${self:provider.region}.kms',
359
+ VpcEndpointType: 'Interface',
360
+ SubnetIds: [
361
+ { Ref: 'FriggPrivateSubnet1' },
362
+ { Ref: 'FriggPrivateSubnet2' }
363
+ ],
364
+ SecurityGroupIds: [
365
+ { Ref: 'FriggVPCEndpointSecurityGroup' }
366
+ ],
367
+ PrivateDnsEnabled: true
368
+ }
369
+ };
370
+ }
371
+
372
+ // Secrets Manager Interface Endpoint (paid, but useful for secrets)
373
+ vpcResources.FriggSecretsManagerVPCEndpoint = {
374
+ Type: 'AWS::EC2::VPCEndpoint',
375
+ Properties: {
376
+ VpcId: { Ref: 'FriggVPC' },
377
+ ServiceName: 'com.amazonaws.${self:provider.region}.secretsmanager',
378
+ VpcEndpointType: 'Interface',
379
+ SubnetIds: [
380
+ { Ref: 'FriggPrivateSubnet1' },
381
+ { Ref: 'FriggPrivateSubnet2' }
382
+ ],
383
+ SecurityGroupIds: [
384
+ { Ref: 'FriggVPCEndpointSecurityGroup' }
385
+ ],
386
+ PrivateDnsEnabled: true
387
+ }
388
+ };
389
+
390
+ // Security Group for VPC Endpoints
391
+ vpcResources.FriggVPCEndpointSecurityGroup = {
392
+ Type: 'AWS::EC2::SecurityGroup',
393
+ Properties: {
394
+ GroupDescription: 'Security group for Frigg VPC Endpoints',
395
+ VpcId: { Ref: 'FriggVPC' },
396
+ SecurityGroupIngress: [
397
+ {
398
+ IpProtocol: 'tcp',
399
+ FromPort: 443,
400
+ ToPort: 443,
401
+ SourceSecurityGroupId: { Ref: 'FriggLambdaSecurityGroup' },
402
+ Description: 'HTTPS from Lambda'
403
+ }
404
+ ],
405
+ Tags: [
406
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-vpc-endpoint-sg' }
407
+ ]
408
+ }
409
+ };
410
+ }
411
+
412
+ return vpcResources;
413
+ };
414
+
105
415
  const composeServerlessDefinition = (AppDefinition) => {
416
+ // Define CORS configuration to be used across all endpoints
417
+ const corsConfig = {
418
+ origin: '*',
419
+ headers: '*',
420
+ methods: ['ANY'],
421
+ allowCredentials: true,
422
+ };
423
+
106
424
  const definition = {
107
425
  frameworkVersion: '>=3.17.0',
108
426
  service: AppDefinition.name || 'create-frigg-app',
@@ -207,21 +525,21 @@ const composeServerlessDefinition = (AppDefinition) => {
207
525
  http: {
208
526
  path: '/api/integrations',
209
527
  method: 'ANY',
210
- cors: true,
528
+ cors: corsConfig,
211
529
  },
212
530
  },
213
531
  {
214
532
  http: {
215
533
  path: '/api/integrations/{proxy+}',
216
534
  method: 'ANY',
217
- cors: true,
535
+ cors: corsConfig,
218
536
  },
219
537
  },
220
538
  {
221
539
  http: {
222
540
  path: '/api/authorize',
223
541
  method: 'ANY',
224
- cors: true,
542
+ cors: corsConfig,
225
543
  },
226
544
  },
227
545
  ],
@@ -233,7 +551,26 @@ const composeServerlessDefinition = (AppDefinition) => {
233
551
  http: {
234
552
  path: '/user/{proxy+}',
235
553
  method: 'ANY',
236
- cors: true,
554
+ cors: corsConfig,
555
+ },
556
+ },
557
+ ],
558
+ },
559
+ health: {
560
+ handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
561
+ events: [
562
+ {
563
+ http: {
564
+ path: '/health',
565
+ method: 'GET',
566
+ cors: corsConfig,
567
+ },
568
+ },
569
+ {
570
+ http: {
571
+ path: '/health/{proxy+}',
572
+ method: 'GET',
573
+ cors: corsConfig,
237
574
  },
238
575
  },
239
576
  ],
@@ -329,28 +666,154 @@ const composeServerlessDefinition = (AppDefinition) => {
329
666
  },
330
667
  };
331
668
 
669
+ // Configure BASE_URL based on custom domain or API Gateway
670
+ if (process.env.CUSTOM_DOMAIN) {
671
+
672
+ // Configure custom domain
673
+ definition.custom.customDomain = {
674
+ domainName: process.env.CUSTOM_DOMAIN,
675
+ basePath: process.env.CUSTOM_BASE_PATH || '',
676
+ stage: '${self:provider.stage}',
677
+ createRoute53Record: process.env.CREATE_ROUTE53_RECORD !== 'false', // Default true
678
+ certificateName: process.env.CERTIFICATE_NAME || process.env.CUSTOM_DOMAIN,
679
+ endpointType: process.env.ENDPOINT_TYPE || 'edge', // edge, regional, or private
680
+ securityPolicy: process.env.SECURITY_POLICY || 'tls_1_2',
681
+ apiType: 'rest',
682
+ autoDomain: process.env.AUTO_DOMAIN === 'true', // Auto create domain if it doesn't exist
683
+ };
684
+
685
+ // Set BASE_URL to custom domain
686
+ definition.provider.environment.BASE_URL = `https://${process.env.CUSTOM_DOMAIN}`;
687
+ } else {
688
+ // Default BASE_URL using API Gateway generated URL
689
+ definition.provider.environment.BASE_URL = {
690
+ 'Fn::Join': [
691
+ '',
692
+ [
693
+ 'https://',
694
+ { Ref: 'ApiGatewayRestApi' },
695
+ '.execute-api.',
696
+ { Ref: 'AWS::Region' },
697
+ '.amazonaws.com/',
698
+ '${self:provider.stage}',
699
+ ],
700
+ ],
701
+ };
702
+ }
703
+
704
+ // REDIRECT_PATH is required for OAuth integrations
705
+ if (!process.env.REDIRECT_PATH) {
706
+ throw new Error(
707
+ 'REDIRECT_PATH environment variable is required. ' +
708
+ 'Please set REDIRECT_PATH in your .env file (e.g., REDIRECT_PATH=/oauth/callback)'
709
+ );
710
+ }
711
+
712
+ // Set REDIRECT_URI based on domain configuration
713
+ if (process.env.CUSTOM_DOMAIN) {
714
+ definition.provider.environment.REDIRECT_URI = `https://${process.env.CUSTOM_DOMAIN}${process.env.REDIRECT_PATH}`;
715
+ } else {
716
+ definition.provider.environment.REDIRECT_URI = {
717
+ 'Fn::Join': [
718
+ '',
719
+ [
720
+ 'https://',
721
+ { Ref: 'ApiGatewayRestApi' },
722
+ '.execute-api.',
723
+ { Ref: 'AWS::Region' },
724
+ '.amazonaws.com/',
725
+ '${self:provider.stage}',
726
+ process.env.REDIRECT_PATH,
727
+ ],
728
+ ],
729
+ };
730
+ }
731
+
732
+ // Add REDIRECT_URI to CloudFormation outputs
733
+ definition.resources.Outputs = {
734
+ RedirectURI: {
735
+ Description: 'OAuth Redirect URI to register with providers',
736
+ Value: definition.provider.environment.REDIRECT_URI,
737
+ },
738
+ };
739
+
332
740
  // KMS Configuration based on App Definition
333
741
  if (AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true) {
334
- // Add KMS IAM permissions
742
+ // Provision a dedicated KMS key and wire it automatically
743
+ definition.resources.Resources.FriggKMSKey = {
744
+ Type: 'AWS::KMS::Key',
745
+ Properties: {
746
+ EnableKeyRotation: true,
747
+ KeyPolicy: {
748
+ Version: '2012-10-17',
749
+ Statement: [
750
+ {
751
+ Sid: 'AllowRootAccountAdmin',
752
+ Effect: 'Allow',
753
+ Principal: { AWS: { 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root' } },
754
+ Action: 'kms:*',
755
+ Resource: '*'
756
+ }
757
+ ]
758
+ }
759
+ }
760
+ };
761
+
335
762
  definition.provider.iamRoleStatements.push({
336
763
  Effect: 'Allow',
337
- Action: [
338
- 'kms:GenerateDataKey',
339
- 'kms:Decrypt'
340
- ],
341
- Resource: ['${self:custom.kmsGrants.kmsKeyId}']
764
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
765
+ Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }]
342
766
  });
343
767
 
344
- // Add KMS_KEY_ARN environment variable for Frigg Encrypt module
345
- definition.provider.environment.KMS_KEY_ARN = '${self:custom.kmsGrants.kmsKeyId}';
768
+ definition.provider.environment.KMS_KEY_ARN = { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] };
346
769
 
347
- // Add serverless-kms-grants plugin
348
770
  definition.plugins.push('serverless-kms-grants');
771
+ definition.custom.kmsGrants = { kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] } };
772
+ }
349
773
 
350
- // Configure KMS grants with default key
351
- definition.custom.kmsGrants = {
352
- kmsKeyId: '*'
353
- };
774
+ // VPC Configuration based on App Definition
775
+ if (AppDefinition.vpc?.enable === true) {
776
+ // Create VPC config from App Definition or use auto-created resources
777
+ const vpcConfig = {};
778
+
779
+ if (AppDefinition.vpc.securityGroupIds) {
780
+ // User provided custom security groups
781
+ vpcConfig.securityGroupIds = AppDefinition.vpc.securityGroupIds;
782
+ } else {
783
+ // Use auto-created security group
784
+ vpcConfig.securityGroupIds = [{ Ref: 'FriggLambdaSecurityGroup' }];
785
+ }
786
+
787
+ if (AppDefinition.vpc.subnetIds) {
788
+ // User provided custom subnets
789
+ vpcConfig.subnetIds = AppDefinition.vpc.subnetIds;
790
+ } else {
791
+ // Use auto-created private subnets
792
+ vpcConfig.subnetIds = [
793
+ { Ref: 'FriggPrivateSubnet1' },
794
+ { Ref: 'FriggPrivateSubnet2' }
795
+ ];
796
+ }
797
+
798
+ // Set VPC config for Lambda functions
799
+ definition.provider.vpc = vpcConfig;
800
+
801
+ // Add VPC-related IAM permissions
802
+ definition.provider.iamRoleStatements.push({
803
+ Effect: 'Allow',
804
+ Action: [
805
+ 'ec2:CreateNetworkInterface',
806
+ 'ec2:DescribeNetworkInterfaces',
807
+ 'ec2:DeleteNetworkInterface',
808
+ 'ec2:AttachNetworkInterface',
809
+ 'ec2:DetachNetworkInterface'
810
+ ],
811
+ Resource: '*'
812
+ });
813
+
814
+ // Add VPC infrastructure resources to CloudFormation
815
+ const vpcResources = createVPCInfrastructure(AppDefinition);
816
+ Object.assign(definition.resources.Resources, vpcResources);
354
817
  }
355
818
 
356
819
  // Add integration-specific functions and resources
@@ -365,7 +828,7 @@ const composeServerlessDefinition = (AppDefinition) => {
365
828
  http: {
366
829
  path: `/api/${integrationName}-integration/{proxy+}`,
367
830
  method: 'ANY',
368
- cors: true,
831
+ cors: corsConfig,
369
832
  },
370
833
  },
371
834
  ],
@@ -425,4 +888,4 @@ const composeServerlessDefinition = (AppDefinition) => {
425
888
  return definition;
426
889
  };
427
890
 
428
- module.exports = { composeServerlessDefinition };
891
+ module.exports = { composeServerlessDefinition };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.397.fe6d7a2.0",
4
+ "version": "2.0.0--canary.405.45825cd.0",
5
5
  "dependencies": {
6
6
  "@babel/eslint-parser": "^7.18.9",
7
7
  "@babel/parser": "^7.25.3",
8
8
  "@babel/traverse": "^7.25.3",
9
- "@friggframework/test": "2.0.0--canary.397.fe6d7a2.0",
9
+ "@friggframework/test": "2.0.0--canary.405.45825cd.0",
10
10
  "@hapi/boom": "^10.0.1",
11
11
  "@inquirer/prompts": "^5.3.8",
12
12
  "axios": "^1.7.2",
@@ -27,8 +27,8 @@
27
27
  "serverless-http": "^2.7.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@friggframework/eslint-config": "2.0.0--canary.397.fe6d7a2.0",
31
- "@friggframework/prettier-config": "2.0.0--canary.397.fe6d7a2.0",
30
+ "@friggframework/eslint-config": "2.0.0--canary.405.45825cd.0",
31
+ "@friggframework/prettier-config": "2.0.0--canary.405.45825cd.0",
32
32
  "prettier": "^2.7.1",
33
33
  "serverless": "3.39.0",
34
34
  "serverless-dotenv-plugin": "^6.0.0",
@@ -60,5 +60,5 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "gitHead": "fe6d7a2d20c22ee67c13948c0af57ed3b72ca481"
63
+ "gitHead": "45825cde018fed76379aae97a58f03c5695f40c9"
64
64
  }
@@ -0,0 +1,125 @@
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
+
package/test/index.js CHANGED
@@ -1,9 +1,11 @@
1
- const { testDefinitionRequiredAuthMethods } = require('./auther-definition-method-tester');
2
- const { createMockIntegration, createMockApiObject } = require('./mock-integration');
1
+ const {testDefinitionRequiredAuthMethods} = require('./auther-definition-method-tester');
2
+ const {createMockIntegration, createMockApiObject} = require('./mock-integration');
3
+ const { testAutherDefinition } = require('./auther-definition-tester');
3
4
 
4
5
 
5
6
  module.exports = {
6
7
  createMockIntegration,
7
8
  createMockApiObject,
8
9
  testDefinitionRequiredAuthMethods,
10
+ testAutherDefinition,
9
11
  };
@@ -1,4 +1,8 @@
1
1
  const {
2
+ Auther,
3
+ Credential,
4
+ Entity,
5
+ IntegrationFactory,
2
6
  createObjectId,
3
7
  } = require('@friggframework/core');
4
8
 
@@ -7,6 +11,7 @@ async function createMockIntegration(
7
11
  userId = null,
8
12
  config = { type: IntegrationClass.Definition.name }
9
13
  ) {
14
+ const integrationFactory = new IntegrationFactory([IntegrationClass]);
10
15
  userId = userId || createObjectId();
11
16
 
12
17
  const insertOptions = {
@@ -19,8 +24,10 @@ async function createMockIntegration(
19
24
  const entities = [];
20
25
  for (const moduleName in IntegrationClass.modules) {
21
26
  const ModuleDef = IntegrationClass.Definition.modules[moduleName];
22
- // todo: create module using the new architecture
23
- const module = {}
27
+ const module = await Auther.getInstance({
28
+ definition: ModuleDef,
29
+ userId: userId,
30
+ });
24
31
  const credential = await module.CredentialModel.findOneAndUpdate(
25
32
  user,
26
33
  { $set: user },
@@ -44,8 +51,11 @@ async function createMockIntegration(
44
51
  );
45
52
  }
46
53
 
47
- // todo: create integration using the new architecture
48
- const integration = {}
54
+ const integration = await integrationFactory.createIntegration(
55
+ entities,
56
+ userId,
57
+ config
58
+ );
49
59
 
50
60
  integration.id = integration.record._id;
51
61