@friggframework/devtools 2.0.0-next.11 → 2.0.0-next.110

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.
Files changed (247) hide show
  1. package/frigg-cli/README.md +1289 -0
  2. package/frigg-cli/__tests__/unit/commands/build.test.js +279 -0
  3. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +649 -0
  4. package/frigg-cli/__tests__/unit/commands/deploy.test.js +320 -0
  5. package/frigg-cli/__tests__/unit/commands/doctor.test.js +309 -0
  6. package/frigg-cli/__tests__/unit/commands/generate-iam.test.js +97 -0
  7. package/frigg-cli/__tests__/unit/commands/install.test.js +400 -0
  8. package/frigg-cli/__tests__/unit/commands/ui.test.js +346 -0
  9. package/frigg-cli/__tests__/unit/dependencies.test.js +74 -0
  10. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +397 -0
  11. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +345 -0
  12. package/frigg-cli/__tests__/unit/version-detection.test.js +171 -0
  13. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  14. package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
  15. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  16. package/frigg-cli/__tests__/utils/test-setup.js +287 -0
  17. package/frigg-cli/auth-command/CLAUDE.md +293 -0
  18. package/frigg-cli/auth-command/README.md +450 -0
  19. package/frigg-cli/auth-command/api-key-flow.js +153 -0
  20. package/frigg-cli/auth-command/auth-tester.js +344 -0
  21. package/frigg-cli/auth-command/credential-storage.js +182 -0
  22. package/frigg-cli/auth-command/index.js +256 -0
  23. package/frigg-cli/auth-command/json-schema-form.js +67 -0
  24. package/frigg-cli/auth-command/module-loader.js +172 -0
  25. package/frigg-cli/auth-command/oauth-callback-server.js +431 -0
  26. package/frigg-cli/auth-command/oauth-flow.js +195 -0
  27. package/frigg-cli/auth-command/utils/browser.js +30 -0
  28. package/frigg-cli/build-command/index.js +45 -12
  29. package/frigg-cli/db-setup-command/index.js +246 -0
  30. package/frigg-cli/deploy-command/SPEC-DEPLOY-DRY-RUN.md +981 -0
  31. package/frigg-cli/deploy-command/index.js +339 -23
  32. package/frigg-cli/doctor-command/index.js +335 -0
  33. package/frigg-cli/generate-command/__tests__/generate-command.test.js +301 -0
  34. package/frigg-cli/generate-command/azure-generator.js +43 -0
  35. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  36. package/frigg-cli/generate-command/index.js +333 -0
  37. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  38. package/frigg-cli/generate-iam-command.js +119 -0
  39. package/frigg-cli/index.js +189 -1
  40. package/frigg-cli/index.test.js +1 -4
  41. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  42. package/frigg-cli/init-command/index.js +93 -0
  43. package/frigg-cli/init-command/template-handler.js +143 -0
  44. package/frigg-cli/install-command/index.js +1 -4
  45. package/frigg-cli/jest.config.js +124 -0
  46. package/frigg-cli/package.json +63 -0
  47. package/frigg-cli/repair-command/index.js +564 -0
  48. package/frigg-cli/ssm-command/index.js +308 -0
  49. package/frigg-cli/ssm-command/index.test.js +318 -0
  50. package/frigg-cli/start-command/index.js +118 -5
  51. package/frigg-cli/start-command/start-command.test.js +297 -0
  52. package/frigg-cli/test/init-command.test.js +180 -0
  53. package/frigg-cli/test/npm-registry.test.js +319 -0
  54. package/frigg-cli/ui-command/index.js +154 -0
  55. package/frigg-cli/utils/app-resolver.js +319 -0
  56. package/frigg-cli/utils/backend-path.js +16 -17
  57. package/frigg-cli/utils/database-validator.js +167 -0
  58. package/frigg-cli/utils/error-messages.js +329 -0
  59. package/frigg-cli/utils/npm-registry.js +167 -0
  60. package/frigg-cli/utils/process-manager.js +199 -0
  61. package/frigg-cli/utils/repo-detection.js +405 -0
  62. package/infrastructure/ARCHITECTURE.md +487 -0
  63. package/infrastructure/CLAUDE.md +481 -0
  64. package/infrastructure/HEALTH.md +468 -0
  65. package/infrastructure/README.md +540 -0
  66. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  67. package/infrastructure/__tests__/helpers/test-utils.js +275 -0
  68. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  69. package/infrastructure/__tests__/scoped-environment.test.js +126 -0
  70. package/infrastructure/__tests__/ssm-preload-node-options.test.js +79 -0
  71. package/infrastructure/__tests__/template-generation.test.js +687 -0
  72. package/infrastructure/create-frigg-infrastructure.js +129 -20
  73. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  74. package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
  75. package/infrastructure/docs/WEBSOCKET-CONFIGURATION.md +105 -0
  76. package/infrastructure/docs/deployment-instructions.md +268 -0
  77. package/infrastructure/docs/generate-iam-command.md +278 -0
  78. package/infrastructure/docs/iam-policy-templates.md +193 -0
  79. package/infrastructure/domains/admin-scripts/admin-script-builder.js +567 -0
  80. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +1017 -0
  81. package/infrastructure/domains/admin-scripts/index.js +5 -0
  82. package/infrastructure/domains/database/aurora-builder.js +857 -0
  83. package/infrastructure/domains/database/aurora-builder.test.js +960 -0
  84. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  85. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  86. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  87. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  88. package/infrastructure/domains/database/migration-builder.js +757 -0
  89. package/infrastructure/domains/database/migration-builder.test.js +454 -0
  90. package/infrastructure/domains/database/migration-resolver.js +163 -0
  91. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  92. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  93. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  94. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  95. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  96. package/infrastructure/domains/health/application/ports/index.js +26 -0
  97. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  98. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  99. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  100. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  101. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  102. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  103. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  104. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  105. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  106. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  107. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  108. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  109. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  110. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  111. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  112. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  113. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  114. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  115. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  116. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  117. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  118. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  119. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  120. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  121. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  122. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  123. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  124. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  125. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  126. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  127. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  128. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  129. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  130. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  131. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  132. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  133. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  134. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  135. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  136. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  137. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  138. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  139. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  140. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  141. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  142. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  143. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  144. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  145. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  146. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  147. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  148. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  149. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  150. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  151. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  152. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  153. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  154. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  155. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  156. package/infrastructure/domains/integration/integration-builder.js +656 -0
  157. package/infrastructure/domains/integration/integration-builder.test.js +1064 -0
  158. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  159. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  160. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  161. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  162. package/infrastructure/domains/networking/vpc-builder.js +2101 -0
  163. package/infrastructure/domains/networking/vpc-builder.test.js +2001 -0
  164. package/infrastructure/domains/networking/vpc-discovery.js +184 -0
  165. package/infrastructure/domains/networking/vpc-discovery.test.js +368 -0
  166. package/infrastructure/domains/networking/vpc-resolver.js +514 -0
  167. package/infrastructure/domains/networking/vpc-resolver.test.js +841 -0
  168. package/infrastructure/domains/parameters/offload-utils.js +190 -0
  169. package/infrastructure/domains/parameters/offload-utils.test.js +193 -0
  170. package/infrastructure/domains/parameters/ssm-builder.js +132 -0
  171. package/infrastructure/domains/parameters/ssm-builder.test.js +346 -0
  172. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  173. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  174. package/infrastructure/domains/scheduler/scheduler-builder.js +247 -0
  175. package/infrastructure/domains/scheduler/scheduler-builder.test.js +118 -0
  176. package/infrastructure/domains/security/iam-generator.js +851 -0
  177. package/infrastructure/domains/security/iam-generator.test.js +287 -0
  178. package/infrastructure/domains/security/kms-builder.js +415 -0
  179. package/infrastructure/domains/security/kms-builder.test.js +392 -0
  180. package/infrastructure/domains/security/kms-discovery.js +80 -0
  181. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  182. package/infrastructure/domains/security/kms-resolver.js +96 -0
  183. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  184. package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +418 -0
  185. package/infrastructure/domains/security/templates/iam-policy-basic.json +218 -0
  186. package/infrastructure/domains/security/templates/iam-policy-full.json +293 -0
  187. package/infrastructure/domains/shared/base-builder.js +112 -0
  188. package/infrastructure/domains/shared/base-resolver.js +186 -0
  189. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  190. package/infrastructure/domains/shared/builder-orchestrator.js +226 -0
  191. package/infrastructure/domains/shared/builder-orchestrator.test.js +258 -0
  192. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  193. package/infrastructure/domains/shared/cloudformation-discovery.js +681 -0
  194. package/infrastructure/domains/shared/cloudformation-discovery.test.js +1320 -0
  195. package/infrastructure/domains/shared/environment-builder.js +192 -0
  196. package/infrastructure/domains/shared/environment-builder.test.js +529 -0
  197. package/infrastructure/domains/shared/function-environments.js +97 -0
  198. package/infrastructure/domains/shared/function-environments.test.js +146 -0
  199. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +579 -0
  200. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +416 -0
  201. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  202. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  203. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  204. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  205. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  206. package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
  207. package/infrastructure/domains/shared/resource-discovery.js +256 -0
  208. package/infrastructure/domains/shared/resource-discovery.test.js +757 -0
  209. package/infrastructure/domains/shared/types/app-definition.js +225 -0
  210. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  211. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  212. package/infrastructure/domains/shared/types/index.js +46 -0
  213. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  214. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  215. package/infrastructure/domains/shared/utilities/base-definition-factory.js +409 -0
  216. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  217. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +319 -0
  218. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  219. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  220. package/infrastructure/domains/shared/utilities/nested-node-modules.js +53 -0
  221. package/infrastructure/domains/shared/utilities/nested-node-modules.test.js +61 -0
  222. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +159 -0
  223. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +444 -0
  224. package/infrastructure/domains/shared/validation/env-validator.js +78 -0
  225. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  226. package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
  227. package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
  228. package/infrastructure/esbuild.config.js +53 -0
  229. package/infrastructure/infrastructure-composer.js +168 -0
  230. package/infrastructure/infrastructure-composer.test.js +2042 -0
  231. package/infrastructure/integration.test.js +381 -0
  232. package/infrastructure/scripts/build-prisma-layer.js +701 -0
  233. package/infrastructure/scripts/build-prisma-layer.test.js +170 -0
  234. package/infrastructure/scripts/build-time-discovery.js +238 -0
  235. package/infrastructure/scripts/build-time-discovery.test.js +379 -0
  236. package/infrastructure/scripts/run-discovery.js +110 -0
  237. package/infrastructure/scripts/verify-prisma-layer.js +72 -0
  238. package/management-ui/README.md +203 -0
  239. package/package.json +48 -16
  240. package/test/index.js +2 -4
  241. package/test/mock-api.js +1 -3
  242. package/test/mock-integration.js +4 -14
  243. package/.eslintrc.json +0 -3
  244. package/CHANGELOG.md +0 -132
  245. package/infrastructure/serverless-template.js +0 -283
  246. package/infrastructure/webpack.config.js +0 -20
  247. package/test/auther-definition-tester.js +0 -125
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Scheduler Builder
3
+ *
4
+ * Domain Layer - Hexagonal Architecture
5
+ *
6
+ * Responsible for:
7
+ * - Creating EventBridge Scheduler ScheduleGroup resource
8
+ * - Creating IAM Role for EventBridge Scheduler to send messages to SQS
9
+ * - Adding necessary IAM statements for Lambda to create/delete schedules
10
+ *
11
+ * This builder enables integrations to schedule one-time jobs (e.g., webhook renewals)
12
+ * using AWS EventBridge Scheduler.
13
+ */
14
+
15
+ const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
16
+ const {
17
+ isScopedEnvironmentActive,
18
+ getIntegrationFunctionNames,
19
+ getAdminFunctionNames,
20
+ } = require('../shared/function-environments');
21
+
22
+ class SchedulerBuilder extends InfrastructureBuilder {
23
+ constructor() {
24
+ super();
25
+ this.name = 'SchedulerBuilder';
26
+ }
27
+
28
+ shouldExecute(appDefinition) {
29
+ // Enable scheduler if explicitly enabled or if any integration has webhooks
30
+ // (webhooks often need renewal scheduling)
31
+ if (appDefinition.scheduler?.enable === true) {
32
+ return true;
33
+ }
34
+
35
+ // Check if any integration has webhooks enabled
36
+ if (Array.isArray(appDefinition.integrations)) {
37
+ return appDefinition.integrations.some(
38
+ (integration) =>
39
+ integration?.Definition?.webhooks?.enabled === true ||
40
+ integration?.Definition?.webhooks === true
41
+ );
42
+ }
43
+
44
+ return false;
45
+ }
46
+
47
+ getDependencies() {
48
+ return ['IntegrationBuilder']; // Needs integration queues to exist
49
+ }
50
+
51
+ validate(appDefinition) {
52
+ const result = new ValidationResult();
53
+ // No validation required - scheduler is optional
54
+ return result;
55
+ }
56
+
57
+ async build(appDefinition, discoveredResources) {
58
+ console.log(`\n[${this.name}] Configuring EventBridge Scheduler...`);
59
+
60
+ const result = {
61
+ functions: {},
62
+ resources: {},
63
+ environment: {},
64
+ custom: {},
65
+ iamStatements: [],
66
+ };
67
+
68
+ // Create ScheduleGroup resource
69
+ this.createScheduleGroup(result);
70
+
71
+ // Create IAM Role for EventBridge Scheduler
72
+ this.createSchedulerExecutionRole(appDefinition, result);
73
+
74
+ // Add IAM statements for Lambda to manage schedules
75
+ this.addSchedulerIamStatements(result);
76
+
77
+ // Add environment variables
78
+ this.addEnvironmentVariables(result, appDefinition);
79
+
80
+ console.log(`[${this.name}] ✅ Scheduler configuration completed`);
81
+ return result;
82
+ }
83
+
84
+ /**
85
+ * Create EventBridge Scheduler ScheduleGroup
86
+ * Uses stage-specific naming to allow multiple deployments in same AWS account
87
+ */
88
+ createScheduleGroup(result) {
89
+ const scheduleGroupName = '${self:service}-${self:provider.stage}-schedules';
90
+
91
+ result.resources.FriggScheduleGroup = {
92
+ Type: 'AWS::Scheduler::ScheduleGroup',
93
+ Properties: {
94
+ Name: scheduleGroupName,
95
+ },
96
+ };
97
+
98
+ console.log(` ✓ Created ScheduleGroup: ${scheduleGroupName}`);
99
+ }
100
+
101
+ /**
102
+ * Create IAM Role for EventBridge Scheduler to send messages to SQS
103
+ */
104
+ createSchedulerExecutionRole(appDefinition, result) {
105
+ // Collect all integration queue ARNs
106
+ const queueArns = [];
107
+ if (Array.isArray(appDefinition.integrations)) {
108
+ appDefinition.integrations.forEach((integration) => {
109
+ const integrationName = integration?.Definition?.name;
110
+ if (integrationName) {
111
+ const capitalizedName =
112
+ integrationName.charAt(0).toUpperCase() +
113
+ integrationName.slice(1);
114
+ queueArns.push({
115
+ 'Fn::GetAtt': [`${capitalizedName}Queue`, 'Arn'],
116
+ });
117
+ }
118
+ });
119
+ }
120
+
121
+ // If no queues found, use a placeholder (shouldn't happen if IntegrationBuilder ran)
122
+ if (queueArns.length === 0) {
123
+ console.warn(
124
+ ' ⚠ No integration queues found for scheduler role'
125
+ );
126
+ queueArns.push('arn:aws:sqs:*:*:*'); // Fallback
127
+ }
128
+
129
+ result.resources.SchedulerExecutionRole = {
130
+ Type: 'AWS::IAM::Role',
131
+ Properties: {
132
+ RoleName:
133
+ '${self:service}-${self:provider.stage}-scheduler-role',
134
+ AssumeRolePolicyDocument: {
135
+ Version: '2012-10-17',
136
+ Statement: [
137
+ {
138
+ Effect: 'Allow',
139
+ Principal: {
140
+ Service: 'scheduler.amazonaws.com',
141
+ },
142
+ Action: 'sts:AssumeRole',
143
+ },
144
+ ],
145
+ },
146
+ Policies: [
147
+ {
148
+ PolicyName: 'SchedulerSQSPolicy',
149
+ PolicyDocument: {
150
+ Version: '2012-10-17',
151
+ Statement: [
152
+ {
153
+ Effect: 'Allow',
154
+ Action: ['sqs:SendMessage'],
155
+ Resource: queueArns,
156
+ },
157
+ ],
158
+ },
159
+ },
160
+ ],
161
+ },
162
+ };
163
+
164
+ console.log(' ✓ Created SchedulerExecutionRole');
165
+ }
166
+
167
+ /**
168
+ * Add IAM statements for Lambda functions to manage schedules
169
+ */
170
+ addSchedulerIamStatements(result) {
171
+ result.iamStatements.push(
172
+ {
173
+ Effect: 'Allow',
174
+ Action: [
175
+ 'scheduler:CreateSchedule',
176
+ 'scheduler:DeleteSchedule',
177
+ 'scheduler:GetSchedule',
178
+ ],
179
+ Resource: {
180
+ 'Fn::Sub': [
181
+ 'arn:aws:scheduler:${AWS::Region}:${AWS::AccountId}:schedule/${GroupName}/*',
182
+ { GroupName: { Ref: 'FriggScheduleGroup' } },
183
+ ],
184
+ },
185
+ },
186
+ {
187
+ Effect: 'Allow',
188
+ Action: ['iam:PassRole'],
189
+ Resource: { 'Fn::GetAtt': ['SchedulerExecutionRole', 'Arn'] },
190
+ Condition: {
191
+ StringEquals: {
192
+ 'iam:PassedToService': 'scheduler.amazonaws.com',
193
+ },
194
+ },
195
+ }
196
+ );
197
+
198
+ console.log(' ✓ Added scheduler IAM statements');
199
+ }
200
+
201
+ /**
202
+ * Add environment variables for scheduler configuration
203
+ */
204
+ addEnvironmentVariables(result, appDefinition = {}) {
205
+ const environment = {
206
+ SCHEDULER_ROLE_ARN: {
207
+ 'Fn::GetAtt': ['SchedulerExecutionRole', 'Arn'],
208
+ },
209
+ SCHEDULE_GROUP_NAME: {
210
+ Ref: 'FriggScheduleGroup',
211
+ },
212
+ };
213
+
214
+ if (!isScopedEnvironmentActive(appDefinition)) {
215
+ Object.assign(result.environment, environment);
216
+ console.log(' ✓ Added scheduler environment variables');
217
+ return;
218
+ }
219
+
220
+ // Consumers: auth (runs integration actions), the executor (runs
221
+ // admin scripts that instantiate integrations), and every
222
+ // integration function. NOT adminScriptRouter — it carries its own
223
+ // admin-scheduler role, set directly by the admin-script builder.
224
+ const targets = [
225
+ 'auth',
226
+ ...getAdminFunctionNames(appDefinition).filter(
227
+ (fnName) => fnName !== 'adminScriptRouter'
228
+ ),
229
+ ...(appDefinition.integrations || []).flatMap(
230
+ getIntegrationFunctionNames
231
+ ),
232
+ ];
233
+
234
+ result.functionEnvironments = result.functionEnvironments || {};
235
+ for (const fnName of targets) {
236
+ result.functionEnvironments[fnName] = {
237
+ ...result.functionEnvironments[fnName],
238
+ ...environment,
239
+ };
240
+ }
241
+ console.log(
242
+ ` ✓ Scoped scheduler environment variables to: ${targets.join(', ')}`
243
+ );
244
+ }
245
+ }
246
+
247
+ module.exports = { SchedulerBuilder };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Tests for Scheduler Builder
3
+ */
4
+
5
+ const { SchedulerBuilder } = require('./scheduler-builder');
6
+
7
+ describe('SchedulerBuilder', () => {
8
+ let schedulerBuilder;
9
+ const originalSkipDiscovery = process.env.FRIGG_SKIP_AWS_DISCOVERY;
10
+
11
+ beforeEach(() => {
12
+ schedulerBuilder = new SchedulerBuilder();
13
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
14
+ });
15
+
16
+ afterEach(() => {
17
+ if (originalSkipDiscovery === undefined) {
18
+ delete process.env.FRIGG_SKIP_AWS_DISCOVERY;
19
+ } else {
20
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = originalSkipDiscovery;
21
+ }
22
+ });
23
+
24
+ describe('shouldExecute()', () => {
25
+ it('runs when explicitly enabled', () => {
26
+ expect(
27
+ schedulerBuilder.shouldExecute({ scheduler: { enable: true } })
28
+ ).toBe(true);
29
+ });
30
+
31
+ it('runs when any integration has webhooks', () => {
32
+ expect(
33
+ schedulerBuilder.shouldExecute({
34
+ integrations: [
35
+ { Definition: { name: 'a', webhooks: true } },
36
+ ],
37
+ })
38
+ ).toBe(true);
39
+ });
40
+
41
+ it('skips otherwise', () => {
42
+ expect(
43
+ schedulerBuilder.shouldExecute({
44
+ integrations: [{ Definition: { name: 'a' } }],
45
+ })
46
+ ).toBe(false);
47
+ });
48
+ });
49
+
50
+ describe('build() environment', () => {
51
+ const scheduledApp = (extra = {}) => ({
52
+ scheduler: { enable: true },
53
+ integrations: [
54
+ { Definition: { name: 'hubspot', webhooks: true } },
55
+ { Definition: { name: 'slack' } },
56
+ ],
57
+ ...extra,
58
+ });
59
+
60
+ it('broadcasts scheduler vars app-wide by default', async () => {
61
+ const result = await schedulerBuilder.build(scheduledApp(), {});
62
+
63
+ expect(result.environment.SCHEDULER_ROLE_ARN).toEqual({
64
+ 'Fn::GetAtt': ['SchedulerExecutionRole', 'Arn'],
65
+ });
66
+ expect(result.environment.SCHEDULE_GROUP_NAME).toEqual({
67
+ Ref: 'FriggScheduleGroup',
68
+ });
69
+ expect(result.functionEnvironments).toBeUndefined();
70
+ });
71
+
72
+ it('scopes scheduler vars to auth, adminScriptExecutor, and integration functions with lambda.scopedEnvironment', async () => {
73
+ const result = await schedulerBuilder.build(
74
+ scheduledApp({
75
+ lambda: { scopedEnvironment: true },
76
+ adminScripts: [{ Definition: { name: 'fix-things' } }],
77
+ }),
78
+ {}
79
+ );
80
+
81
+ expect(result.environment.SCHEDULER_ROLE_ARN).toBeUndefined();
82
+ expect(result.environment.SCHEDULE_GROUP_NAME).toBeUndefined();
83
+
84
+ const scoped = result.functionEnvironments;
85
+ for (const fnName of [
86
+ 'auth',
87
+ 'adminScriptExecutor',
88
+ 'hubspot',
89
+ 'hubspotWebhook',
90
+ 'hubspotQueueWorker',
91
+ 'slack',
92
+ 'slackQueueWorker',
93
+ ]) {
94
+ expect(scoped[fnName].SCHEDULER_ROLE_ARN).toEqual({
95
+ 'Fn::GetAtt': ['SchedulerExecutionRole', 'Arn'],
96
+ });
97
+ expect(scoped[fnName].SCHEDULE_GROUP_NAME).toEqual({
98
+ Ref: 'FriggScheduleGroup',
99
+ });
100
+ }
101
+
102
+ // the router keeps its own admin-scheduler role, set directly by
103
+ // the admin-script builder — never targeted here
104
+ expect(scoped.adminScriptRouter).toBeUndefined();
105
+ });
106
+
107
+ it('keeps broadcasting in local mode even with the flag on', async () => {
108
+ process.env.FRIGG_SKIP_AWS_DISCOVERY = 'true';
109
+ const result = await schedulerBuilder.build(
110
+ scheduledApp({ lambda: { scopedEnvironment: true } }),
111
+ {}
112
+ );
113
+
114
+ expect(result.environment.SCHEDULER_ROLE_ARN).toBeDefined();
115
+ expect(result.functionEnvironments).toBeUndefined();
116
+ });
117
+ });
118
+ });