@friggframework/devtools 2.0.0--canary.517.b384239.0 → 2.0.0--canary.517.a353e20.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.
@@ -9,6 +9,8 @@
9
9
  * - Creating Lambda function for admin API routes (router)
10
10
  * - Creating EventBridge Scheduler resources (Phase 2)
11
11
  * - Creating IAM roles for scheduler to invoke Lambda
12
+ * - Granting the router/executor Lambdas IAM permission to send to the queue
13
+ * and (when scheduling is enabled) manage EventBridge schedules
12
14
  */
13
15
 
14
16
  const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
@@ -105,6 +107,21 @@ class AdminScriptBuilder extends InfrastructureBuilder {
105
107
  };
106
108
 
107
109
  result.environment.ADMIN_SCRIPT_QUEUE_URL = { Ref: 'AdminScriptQueue' };
110
+
111
+ // The router enqueues async executions and scripts enqueue continuations
112
+ // via queueScript()/queueScriptBatch(). The base role's wildcard does not
113
+ // cover this queue's name, so grant SendMessage explicitly.
114
+ result.iamStatements.push({
115
+ Effect: 'Allow',
116
+ Action: [
117
+ 'sqs:SendMessage',
118
+ 'sqs:SendMessageBatch',
119
+ 'sqs:GetQueueUrl',
120
+ 'sqs:GetQueueAttributes',
121
+ ],
122
+ Resource: { 'Fn::GetAtt': ['AdminScriptQueue', 'Arn'] },
123
+ });
124
+
108
125
  console.log(' ✓ Created AdminScriptQueue');
109
126
  }
110
127
 
@@ -205,6 +222,37 @@ class AdminScriptBuilder extends InfrastructureBuilder {
205
222
  'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'],
206
223
  };
207
224
 
225
+ // The router manages schedules through the AWS scheduler adapter, so it
226
+ // needs scheduler:* on this group plus iam:PassRole for the role it hands
227
+ // to EventBridge. (UpdateSchedule covers the upsert conflict path.)
228
+ result.iamStatements.push(
229
+ {
230
+ Effect: 'Allow',
231
+ Action: [
232
+ 'scheduler:CreateSchedule',
233
+ 'scheduler:UpdateSchedule',
234
+ 'scheduler:DeleteSchedule',
235
+ 'scheduler:GetSchedule',
236
+ ],
237
+ Resource: {
238
+ 'Fn::Sub': [
239
+ 'arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/${GroupName}/*',
240
+ { GroupName: { Ref: 'AdminScriptScheduleGroup' } },
241
+ ],
242
+ },
243
+ },
244
+ {
245
+ Effect: 'Allow',
246
+ Action: ['iam:PassRole'],
247
+ Resource: { 'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'] },
248
+ Condition: {
249
+ StringEquals: {
250
+ 'iam:PassedToService': 'scheduler.amazonaws.com',
251
+ },
252
+ },
253
+ }
254
+ );
255
+
208
256
  console.log(' ✓ Created EventBridge Scheduler resources');
209
257
  }
210
258
  }
@@ -500,6 +500,79 @@ describe('AdminScriptBuilder', () => {
500
500
  Resource: { 'Fn::GetAtt': ['AdminScriptExecutorLambdaFunction', 'Arn'] },
501
501
  });
502
502
  });
503
+
504
+ it('should grant the router SendMessage on AdminScriptQueue', async () => {
505
+ const appDefinition = {
506
+ adminScripts: [{ Definition: { name: 'test-script' } }],
507
+ };
508
+
509
+ const result = await adminScriptBuilder.build(appDefinition, {});
510
+
511
+ const sqsGrant = result.iamStatements.find(
512
+ (s) =>
513
+ Array.isArray(s.Action) &&
514
+ s.Action.includes('sqs:SendMessage')
515
+ );
516
+ expect(sqsGrant).toBeDefined();
517
+ expect(sqsGrant.Action).toContain('sqs:SendMessageBatch');
518
+ expect(sqsGrant.Resource).toEqual({
519
+ 'Fn::GetAtt': ['AdminScriptQueue', 'Arn'],
520
+ });
521
+ });
522
+
523
+ it('should grant scheduler:* + iam:PassRole when scheduling is enabled', async () => {
524
+ const appDefinition = {
525
+ adminScripts: [{ Definition: { name: 'test-script' } }],
526
+ admin: { enableScheduling: true },
527
+ };
528
+
529
+ const result = await adminScriptBuilder.build(appDefinition, {});
530
+
531
+ const schedulerGrant = result.iamStatements.find(
532
+ (s) =>
533
+ Array.isArray(s.Action) &&
534
+ s.Action.includes('scheduler:CreateSchedule')
535
+ );
536
+ expect(schedulerGrant).toBeDefined();
537
+ expect(schedulerGrant.Action).toEqual(
538
+ expect.arrayContaining([
539
+ 'scheduler:CreateSchedule',
540
+ 'scheduler:UpdateSchedule',
541
+ 'scheduler:DeleteSchedule',
542
+ 'scheduler:GetSchedule',
543
+ ])
544
+ );
545
+ expect(schedulerGrant.Resource['Fn::Sub'][1]).toEqual({
546
+ GroupName: { Ref: 'AdminScriptScheduleGroup' },
547
+ });
548
+
549
+ const passRole = result.iamStatements.find(
550
+ (s) =>
551
+ Array.isArray(s.Action) && s.Action.includes('iam:PassRole')
552
+ );
553
+ expect(passRole).toBeDefined();
554
+ expect(passRole.Resource).toEqual({
555
+ 'Fn::GetAtt': ['AdminScriptSchedulerRole', 'Arn'],
556
+ });
557
+ expect(passRole.Condition.StringEquals['iam:PassedToService']).toBe(
558
+ 'scheduler.amazonaws.com'
559
+ );
560
+ });
561
+
562
+ it('should add no scheduler IAM statements when scheduling is disabled', async () => {
563
+ const appDefinition = {
564
+ adminScripts: [{ Definition: { name: 'test-script' } }],
565
+ };
566
+
567
+ const result = await adminScriptBuilder.build(appDefinition, {});
568
+
569
+ const schedulerGrant = result.iamStatements.find(
570
+ (s) =>
571
+ Array.isArray(s.Action) &&
572
+ s.Action.some((a) => a.startsWith('scheduler:'))
573
+ );
574
+ expect(schedulerGrant).toBeUndefined();
575
+ });
503
576
  });
504
577
 
505
578
  describe('getName()', () => {
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.517.b384239.0",
4
+ "version": "2.0.0--canary.517.a353e20.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.517.b384239.0",
29
- "@friggframework/schemas": "2.0.0--canary.517.b384239.0",
30
- "@friggframework/test": "2.0.0--canary.517.b384239.0",
28
+ "@friggframework/core": "2.0.0--canary.517.a353e20.0",
29
+ "@friggframework/schemas": "2.0.0--canary.517.a353e20.0",
30
+ "@friggframework/test": "2.0.0--canary.517.a353e20.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.517.b384239.0",
59
- "@friggframework/prettier-config": "2.0.0--canary.517.b384239.0",
58
+ "@friggframework/eslint-config": "2.0.0--canary.517.a353e20.0",
59
+ "@friggframework/prettier-config": "2.0.0--canary.517.a353e20.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": "b384239f327baa975cb82c0f63888e67f40160c3"
91
+ "gitHead": "a353e204564567f210a23149f4fa7fdb6d779d72"
92
92
  }