@friggframework/devtools 2.0.0--canary.608.e6b65ff.0 → 2.0.0--canary.610.ca077d5.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.
- package/infrastructure/domains/integration/integration-builder.js +0 -95
- package/infrastructure/domains/integration/integration-builder.test.js +1 -119
- package/infrastructure/domains/security/iam-generator.js +0 -2
- package/infrastructure/domains/security/iam-generator.test.js +0 -16
- package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +0 -2
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +0 -2
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +0 -5
- package/package.json +7 -7
|
@@ -196,8 +196,6 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
196
196
|
);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
this.createUserActionQueue(result, functionPackageConfig, usePrismaLayer);
|
|
200
|
-
|
|
201
199
|
for (const integration of appDefinition.integrations) {
|
|
202
200
|
const integrationName = integration.Definition.name;
|
|
203
201
|
const queueDecision = decisions.integrations[integrationName].queue;
|
|
@@ -474,99 +472,6 @@ class IntegrationBuilder extends InfrastructureBuilder {
|
|
|
474
472
|
console.log(' ✓ Created InternalErrorQueue resource');
|
|
475
473
|
}
|
|
476
474
|
|
|
477
|
-
/**
|
|
478
|
-
* App-level FIFO queue + DLQ + worker for `dispatch: 'queue'` events. One
|
|
479
|
-
* queue serves all integrations; MessageGroupId = integrationId serializes
|
|
480
|
-
* per integration. Always created, like the InternalErrorQueue.
|
|
481
|
-
*/
|
|
482
|
-
createUserActionQueue(result, functionPackageConfig, usePrismaLayer = true) {
|
|
483
|
-
const queueName =
|
|
484
|
-
'${self:service}-${self:provider.stage}-FriggUserActionQueue.fifo';
|
|
485
|
-
const dlqName =
|
|
486
|
-
'${self:service}-${self:provider.stage}-FriggUserActionDLQ.fifo';
|
|
487
|
-
|
|
488
|
-
result.custom.FriggUserActionQueue = queueName;
|
|
489
|
-
result.custom.FriggUserActionDLQ = dlqName;
|
|
490
|
-
|
|
491
|
-
// A FIFO source queue can only redrive to a FIFO DLQ.
|
|
492
|
-
result.resources.FriggUserActionDLQ = {
|
|
493
|
-
Type: 'AWS::SQS::Queue',
|
|
494
|
-
Properties: {
|
|
495
|
-
QueueName: '${self:custom.FriggUserActionDLQ}',
|
|
496
|
-
FifoQueue: true,
|
|
497
|
-
MessageRetentionPeriod: 1209600, // 14 days
|
|
498
|
-
},
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
// No ContentBasedDeduplication: the producer sends a unique dedup id so
|
|
502
|
-
// two distinct requests with identical bodies both run (not deduped).
|
|
503
|
-
result.resources.FriggUserActionQueue = {
|
|
504
|
-
Type: 'AWS::SQS::Queue',
|
|
505
|
-
Properties: {
|
|
506
|
-
QueueName: '${self:custom.FriggUserActionQueue}',
|
|
507
|
-
FifoQueue: true,
|
|
508
|
-
MessageRetentionPeriod: 345600, // 4 days
|
|
509
|
-
VisibilityTimeout: 1800, // >= worker timeout (900s)
|
|
510
|
-
RedrivePolicy: {
|
|
511
|
-
maxReceiveCount: 2,
|
|
512
|
-
deadLetterTargetArn: {
|
|
513
|
-
'Fn::GetAtt': ['FriggUserActionDLQ', 'Arn'],
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
},
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
result.environment.USER_ACTION_QUEUE_URL = {
|
|
520
|
-
Ref: 'FriggUserActionQueue',
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
result.resources.FriggUserActionDLQAlarm = {
|
|
524
|
-
Type: 'AWS::CloudWatch::Alarm',
|
|
525
|
-
Properties: {
|
|
526
|
-
AlarmDescription:
|
|
527
|
-
'Messages in FriggUserActionDLQ — eventual (queued) integration event failures',
|
|
528
|
-
Namespace: 'AWS/SQS',
|
|
529
|
-
MetricName: 'ApproximateNumberOfMessagesVisible',
|
|
530
|
-
Statistic: 'Maximum',
|
|
531
|
-
Threshold: 0,
|
|
532
|
-
ComparisonOperator: 'GreaterThanThreshold',
|
|
533
|
-
EvaluationPeriods: 1,
|
|
534
|
-
Period: 300,
|
|
535
|
-
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
536
|
-
Dimensions: [
|
|
537
|
-
{
|
|
538
|
-
Name: 'QueueName',
|
|
539
|
-
Value: {
|
|
540
|
-
'Fn::GetAtt': ['FriggUserActionDLQ', 'QueueName'],
|
|
541
|
-
},
|
|
542
|
-
},
|
|
543
|
-
],
|
|
544
|
-
},
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
// No reservedConcurrency: FIFO already serializes per group; capping
|
|
548
|
-
// would serialize across all integrations.
|
|
549
|
-
result.functions.userActionQueueWorker = {
|
|
550
|
-
handler:
|
|
551
|
-
'node_modules/@friggframework/core/handlers/workers/user-action-worker.userActionQueueWorker',
|
|
552
|
-
skipEsbuild: true,
|
|
553
|
-
package: functionPackageConfig,
|
|
554
|
-
...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }),
|
|
555
|
-
timeout: 900,
|
|
556
|
-
events: [
|
|
557
|
-
{
|
|
558
|
-
sqs: {
|
|
559
|
-
arn: { 'Fn::GetAtt': ['FriggUserActionQueue', 'Arn'] },
|
|
560
|
-
batchSize: 1,
|
|
561
|
-
functionResponseType: 'ReportBatchItemFailures',
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
],
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
console.log(' ✓ Created FriggUserActionQueue (.fifo) + DLQ + worker');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
475
|
/**
|
|
571
476
|
* Use external InternalErrorQueue
|
|
572
477
|
*/
|
|
@@ -751,10 +751,9 @@ describe('IntegrationBuilder', () => {
|
|
|
751
751
|
|
|
752
752
|
const functionKeys = Object.keys(result.functions);
|
|
753
753
|
|
|
754
|
-
//
|
|
754
|
+
// Expected order: dlqProcessor (from InternalErrorQueue), webhook, integration, queueWorker
|
|
755
755
|
expect(functionKeys).toEqual([
|
|
756
756
|
'dlqProcessor',
|
|
757
|
-
'userActionQueueWorker',
|
|
758
757
|
'testWebhook',
|
|
759
758
|
'test',
|
|
760
759
|
'testQueueWorker',
|
|
@@ -936,122 +935,5 @@ describe('IntegrationBuilder', () => {
|
|
|
936
935
|
);
|
|
937
936
|
});
|
|
938
937
|
});
|
|
939
|
-
|
|
940
|
-
describe('User-Action FIFO queue', () => {
|
|
941
|
-
const appDefinition = {
|
|
942
|
-
integrations: [{ Definition: { name: 'test' } }],
|
|
943
|
-
};
|
|
944
|
-
|
|
945
|
-
it('creates a FIFO queue without ContentBasedDeduplication', async () => {
|
|
946
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
947
|
-
|
|
948
|
-
const q = result.resources.FriggUserActionQueue;
|
|
949
|
-
expect(q).toBeDefined();
|
|
950
|
-
expect(q.Type).toBe('AWS::SQS::Queue');
|
|
951
|
-
expect(q.Properties.FifoQueue).toBe(true);
|
|
952
|
-
expect(q.Properties.ContentBasedDeduplication).toBeUndefined();
|
|
953
|
-
expect(q.Properties.VisibilityTimeout).toBe(1800);
|
|
954
|
-
expect(q.Properties.MessageRetentionPeriod).toBe(345600);
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
it('redrives to the FIFO DLQ with maxReceiveCount 2', async () => {
|
|
958
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
959
|
-
|
|
960
|
-
expect(
|
|
961
|
-
result.resources.FriggUserActionQueue.Properties.RedrivePolicy
|
|
962
|
-
).toEqual({
|
|
963
|
-
maxReceiveCount: 2,
|
|
964
|
-
deadLetterTargetArn: {
|
|
965
|
-
'Fn::GetAtt': ['FriggUserActionDLQ', 'Arn'],
|
|
966
|
-
},
|
|
967
|
-
});
|
|
968
|
-
});
|
|
969
|
-
|
|
970
|
-
it('creates a FIFO DLQ with 14-day retention', async () => {
|
|
971
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
972
|
-
|
|
973
|
-
const dlq = result.resources.FriggUserActionDLQ;
|
|
974
|
-
expect(dlq).toBeDefined();
|
|
975
|
-
expect(dlq.Properties.FifoQueue).toBe(true);
|
|
976
|
-
expect(dlq.Properties.MessageRetentionPeriod).toBe(1209600);
|
|
977
|
-
});
|
|
978
|
-
|
|
979
|
-
it('creates the worker bound to the FIFO queue ARN with no reservedConcurrency', async () => {
|
|
980
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
981
|
-
|
|
982
|
-
const worker = result.functions.userActionQueueWorker;
|
|
983
|
-
expect(worker).toBeDefined();
|
|
984
|
-
expect(worker.handler).toBe(
|
|
985
|
-
'node_modules/@friggframework/core/handlers/workers/user-action-worker.userActionQueueWorker'
|
|
986
|
-
);
|
|
987
|
-
expect(worker.timeout).toBe(900);
|
|
988
|
-
expect(worker.reservedConcurrency).toBeUndefined();
|
|
989
|
-
expect(worker.events).toEqual([
|
|
990
|
-
{
|
|
991
|
-
sqs: {
|
|
992
|
-
arn: { 'Fn::GetAtt': ['FriggUserActionQueue', 'Arn'] },
|
|
993
|
-
batchSize: 1,
|
|
994
|
-
functionResponseType: 'ReportBatchItemFailures',
|
|
995
|
-
},
|
|
996
|
-
},
|
|
997
|
-
]);
|
|
998
|
-
});
|
|
999
|
-
|
|
1000
|
-
it('exposes USER_ACTION_QUEUE_URL to all Lambdas', async () => {
|
|
1001
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
1002
|
-
|
|
1003
|
-
expect(result.environment.USER_ACTION_QUEUE_URL).toEqual({
|
|
1004
|
-
Ref: 'FriggUserActionQueue',
|
|
1005
|
-
});
|
|
1006
|
-
});
|
|
1007
|
-
|
|
1008
|
-
it('alarms on the FIFO DLQ via the InternalErrorBridgeTopic', async () => {
|
|
1009
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
1010
|
-
|
|
1011
|
-
const alarm = result.resources.FriggUserActionDLQAlarm;
|
|
1012
|
-
expect(alarm).toBeDefined();
|
|
1013
|
-
expect(alarm.Properties.AlarmActions).toEqual([
|
|
1014
|
-
{ Ref: 'InternalErrorBridgeTopic' },
|
|
1015
|
-
]);
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
|
-
it('creates exactly one FIFO queue + worker regardless of integration count', async () => {
|
|
1019
|
-
const result = await integrationBuilder.build(
|
|
1020
|
-
{
|
|
1021
|
-
integrations: [
|
|
1022
|
-
{ Definition: { name: 'hubspot' } },
|
|
1023
|
-
{ Definition: { name: 'salesforce' } },
|
|
1024
|
-
{ Definition: { name: 'slack' } },
|
|
1025
|
-
],
|
|
1026
|
-
},
|
|
1027
|
-
{}
|
|
1028
|
-
);
|
|
1029
|
-
|
|
1030
|
-
const fifoQueues = Object.keys(result.resources).filter(
|
|
1031
|
-
(k) => k === 'FriggUserActionQueue'
|
|
1032
|
-
);
|
|
1033
|
-
expect(fifoQueues).toHaveLength(1);
|
|
1034
|
-
expect(result.functions.userActionQueueWorker).toBeDefined();
|
|
1035
|
-
});
|
|
1036
|
-
|
|
1037
|
-
it('does not make per-integration queues FIFO', async () => {
|
|
1038
|
-
const result = await integrationBuilder.build(appDefinition, {});
|
|
1039
|
-
|
|
1040
|
-
expect(
|
|
1041
|
-
result.resources.TestQueue.Properties.FifoQueue
|
|
1042
|
-
).toBeUndefined();
|
|
1043
|
-
});
|
|
1044
|
-
|
|
1045
|
-
it('omits the Prisma layer on the worker when usePrismaLambdaLayer=false', async () => {
|
|
1046
|
-
const result = await integrationBuilder.build(
|
|
1047
|
-
{ usePrismaLambdaLayer: false, integrations: [{ Definition: { name: 'test' } }] },
|
|
1048
|
-
{}
|
|
1049
|
-
);
|
|
1050
|
-
|
|
1051
|
-
expect(
|
|
1052
|
-
result.functions.userActionQueueWorker.layers
|
|
1053
|
-
).toBeUndefined();
|
|
1054
|
-
});
|
|
1055
|
-
});
|
|
1056
938
|
});
|
|
1057
939
|
|
|
@@ -426,8 +426,6 @@ function generateIAMCloudFormation(options = {}) {
|
|
|
426
426
|
],
|
|
427
427
|
Resource: [
|
|
428
428
|
{ 'Fn::Sub': 'arn:aws:sqs:*:${AWS::AccountId}:*frigg*' },
|
|
429
|
-
// Case-sensitive: lowercase "*frigg*" won't match "FriggUserActionQueue.fifo".
|
|
430
|
-
{ 'Fn::Sub': 'arn:aws:sqs:*:${AWS::AccountId}:*Frigg*' },
|
|
431
429
|
{
|
|
432
430
|
'Fn::Sub':
|
|
433
431
|
'arn:aws:sqs:*:${AWS::AccountId}:internal-error-queue-*',
|
|
@@ -182,22 +182,6 @@ describe('IAM Generator', () => {
|
|
|
182
182
|
expect(yaml).toContain('internal-error-queue-*');
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
-
it('should include a case-sensitive *Frigg* SQS resource for the FIFO queue', () => {
|
|
186
|
-
const appDefinition = {
|
|
187
|
-
name: 'test-app',
|
|
188
|
-
integrations: [],
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const summary = getFeatureSummary(appDefinition);
|
|
192
|
-
const yaml = generateIAMCloudFormation({
|
|
193
|
-
appName: summary.appName,
|
|
194
|
-
features: summary.features,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// The lowercase "*frigg*" glob would not match "...-FriggUserActionQueue.fifo".
|
|
198
|
-
expect(yaml).toContain(':*Frigg*');
|
|
199
|
-
});
|
|
200
|
-
|
|
201
185
|
it('should generate outputs section', () => {
|
|
202
186
|
const appDefinition = {
|
|
203
187
|
name: 'test-app',
|
|
@@ -205,8 +205,6 @@ Resources:
|
|
|
205
205
|
- 'sqs:UntagQueue'
|
|
206
206
|
Resource:
|
|
207
207
|
- !Sub 'arn:aws:sqs:*:${AWS::AccountId}:*frigg*'
|
|
208
|
-
# Case-sensitive: lowercase "*frigg*" won't match "FriggUserActionQueue.fifo".
|
|
209
|
-
- !Sub 'arn:aws:sqs:*:${AWS::AccountId}:*Frigg*'
|
|
210
208
|
- !Sub 'arn:aws:sqs:*:${AWS::AccountId}:internal-error-queue-*'
|
|
211
209
|
|
|
212
210
|
# SNS permissions
|
|
@@ -196,8 +196,6 @@ function createBaseDefinition(
|
|
|
196
196
|
],
|
|
197
197
|
Resource: [
|
|
198
198
|
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
199
|
-
// Explicit: the "-*Queue" glob below doesn't match ".fifo".
|
|
200
|
-
{ 'Fn::GetAtt': ['FriggUserActionQueue', 'Arn'] },
|
|
201
199
|
{
|
|
202
200
|
'Fn::Join': [
|
|
203
201
|
':',
|
|
@@ -133,11 +133,6 @@ describe('Base Definition Factory', () => {
|
|
|
133
133
|
stmt => stmt.Action.includes('sqs:SendMessage')
|
|
134
134
|
);
|
|
135
135
|
expect(sqsPermission).toBeDefined();
|
|
136
|
-
|
|
137
|
-
// The "-*Queue" glob doesn't match ".fifo", so the ARN is explicit.
|
|
138
|
-
expect(sqsPermission.Resource).toContainEqual({
|
|
139
|
-
'Fn::GetAtt': ['FriggUserActionQueue', 'Arn'],
|
|
140
|
-
});
|
|
141
136
|
});
|
|
142
137
|
|
|
143
138
|
it('should include required plugins', () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/devtools",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.
|
|
4
|
+
"version": "2.0.0--canary.610.ca077d5.0",
|
|
5
5
|
"bin": {
|
|
6
6
|
"frigg": "./frigg-cli/index.js"
|
|
7
7
|
},
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"@babel/eslint-parser": "^7.18.9",
|
|
26
26
|
"@babel/parser": "^7.25.3",
|
|
27
27
|
"@babel/traverse": "^7.25.3",
|
|
28
|
-
"@friggframework/core": "2.0.0--canary.
|
|
29
|
-
"@friggframework/schemas": "2.0.0--canary.
|
|
30
|
-
"@friggframework/test": "2.0.0--canary.
|
|
28
|
+
"@friggframework/core": "2.0.0--canary.610.ca077d5.0",
|
|
29
|
+
"@friggframework/schemas": "2.0.0--canary.610.ca077d5.0",
|
|
30
|
+
"@friggframework/test": "2.0.0--canary.610.ca077d5.0",
|
|
31
31
|
"@hapi/boom": "^10.0.1",
|
|
32
32
|
"@inquirer/prompts": "^5.3.8",
|
|
33
33
|
"axios": "^1.18.0",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"validate-npm-package-name": "^5.0.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@friggframework/eslint-config": "2.0.0--canary.
|
|
59
|
-
"@friggframework/prettier-config": "2.0.0--canary.
|
|
58
|
+
"@friggframework/eslint-config": "2.0.0--canary.610.ca077d5.0",
|
|
59
|
+
"@friggframework/prettier-config": "2.0.0--canary.610.ca077d5.0",
|
|
60
60
|
"aws-sdk-client-mock": "^4.1.0",
|
|
61
61
|
"aws-sdk-client-mock-jest": "^4.1.0",
|
|
62
62
|
"jest": "^30.1.3",
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"publishConfig": {
|
|
89
89
|
"access": "public"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "ca077d5f47ea8b69880c3adeebbdbd3a6ac5abb5"
|
|
92
92
|
}
|