@friggframework/admin-scripts 2.0.0--canary.517.06fe141.0 → 2.0.0--canary.517.b384239.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/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@friggframework/admin-scripts",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.517.06fe141.0",
4
+ "version": "2.0.0--canary.517.b384239.0",
5
5
  "description": "Admin Script Runner for Frigg - Execute maintenance and operational scripts in hosted environments",
6
6
  "dependencies": {
7
7
  "@aws-sdk/client-scheduler": "^3.588.0",
8
- "@friggframework/core": "2.0.0--canary.517.06fe141.0",
8
+ "@friggframework/core": "2.0.0--canary.517.b384239.0",
9
9
  "@hapi/boom": "^10.0.1",
10
10
  "express": "^4.18.2",
11
11
  "serverless-http": "^3.2.0"
12
12
  },
13
13
  "devDependencies": {
14
- "@friggframework/eslint-config": "2.0.0--canary.517.06fe141.0",
15
- "@friggframework/prettier-config": "2.0.0--canary.517.06fe141.0",
16
- "@friggframework/test": "2.0.0--canary.517.06fe141.0",
14
+ "@friggframework/eslint-config": "2.0.0--canary.517.b384239.0",
15
+ "@friggframework/prettier-config": "2.0.0--canary.517.b384239.0",
16
+ "@friggframework/test": "2.0.0--canary.517.b384239.0",
17
17
  "eslint": "^8.22.0",
18
18
  "jest": "^29.7.0",
19
19
  "prettier": "^2.7.1",
@@ -44,5 +44,5 @@
44
44
  "maintenance",
45
45
  "operations"
46
46
  ],
47
- "gitHead": "06fe14103a72028ce7bf622631580fb24a4abaaf"
47
+ "gitHead": "b384239f327baa975cb82c0f63888e67f40160c3"
48
48
  }
@@ -1,4 +1,7 @@
1
- const { createSchedulerAdapter } = require('../scheduler-adapter-factory');
1
+ const {
2
+ createSchedulerAdapter,
3
+ createSchedulerAdapterFromEnv,
4
+ } = require('../scheduler-adapter-factory');
2
5
  const { AWSSchedulerAdapter } = require('../aws-scheduler-adapter');
3
6
  const { LocalSchedulerAdapter } = require('../local-scheduler-adapter');
4
7
 
@@ -135,4 +138,51 @@ describe('Scheduler Adapter Factory', () => {
135
138
  ).toThrow();
136
139
  });
137
140
  });
141
+
142
+ describe('createSchedulerAdapterFromEnv()', () => {
143
+ it('falls back to the local adapter off-AWS when SCHEDULER_PROVIDER is unset', () => {
144
+ delete process.env.SCHEDULER_PROVIDER;
145
+ delete process.env.AWS_LAMBDA_FUNCTION_NAME;
146
+
147
+ const adapter = createSchedulerAdapterFromEnv();
148
+
149
+ expect(adapter).toBeInstanceOf(LocalSchedulerAdapter);
150
+ });
151
+
152
+ it('throws 503 when SCHEDULER_PROVIDER is unset in a deployed Lambda', () => {
153
+ delete process.env.SCHEDULER_PROVIDER;
154
+ process.env.AWS_LAMBDA_FUNCTION_NAME = 'admin-script-router';
155
+
156
+ let error;
157
+ try {
158
+ createSchedulerAdapterFromEnv();
159
+ } catch (e) {
160
+ error = e;
161
+ }
162
+
163
+ expect(error).toBeDefined();
164
+ expect(error.isBoom).toBe(true);
165
+ expect(error.output.statusCode).toBe(503);
166
+ });
167
+
168
+ it('builds the AWS adapter from SCHEDULER_PROVIDER + ADMIN_SCRIPT_* env', () => {
169
+ process.env.SCHEDULER_PROVIDER = 'aws';
170
+ process.env.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN =
171
+ awsAdapterParams.targetLambdaArn;
172
+ process.env.ADMIN_SCRIPT_SCHEDULE_GROUP =
173
+ awsAdapterParams.scheduleGroupName;
174
+ process.env.SCHEDULER_ROLE_ARN = awsAdapterParams.roleArn;
175
+
176
+ const adapter = createSchedulerAdapterFromEnv();
177
+
178
+ expect(adapter).toBeInstanceOf(AWSSchedulerAdapter);
179
+ expect(adapter.targetLambdaArn).toBe(
180
+ awsAdapterParams.targetLambdaArn
181
+ );
182
+ expect(adapter.scheduleGroupName).toBe(
183
+ awsAdapterParams.scheduleGroupName
184
+ );
185
+ expect(adapter.roleArn).toBe(awsAdapterParams.roleArn);
186
+ });
187
+ });
138
188
  });
@@ -1,13 +1,16 @@
1
+ const Boom = require('@hapi/boom');
1
2
  const { AWSSchedulerAdapter } = require('./aws-scheduler-adapter');
2
3
  const { LocalSchedulerAdapter } = require('./local-scheduler-adapter');
3
4
 
4
5
  /**
5
6
  * Scheduler Adapter Factory
6
7
  *
7
- * Application Layer - Hexagonal Architecture
8
+ * Infrastructure Layer - Hexagonal Architecture
8
9
  *
9
- * Creates the appropriate scheduler adapter based on explicit configuration
10
- * from appDefinition. Does not auto-detect or read environment variables.
10
+ * `createSchedulerAdapter` builds an adapter from an explicit `type` (no env
11
+ * reads). `createSchedulerAdapterFromEnv` resolves the type from the runtime
12
+ * environment and enforces that a deployed Lambda never silently falls back to
13
+ * the in-memory local adapter.
11
14
  */
12
15
 
13
16
  /**
@@ -46,6 +49,37 @@ function createSchedulerAdapter(options = {}) {
46
49
  }
47
50
  }
48
51
 
52
+ /**
53
+ * Resolve and build the scheduler adapter from the runtime environment.
54
+ *
55
+ * The local adapter is in-memory only (schedules vanish on cold start), so it
56
+ * must never be the silent default in a deployed Lambda: require an explicit
57
+ * SCHEDULER_PROVIDER when running on AWS, and fall back to 'local' only for
58
+ * local dev/tests.
59
+ *
60
+ * @returns {SchedulerAdapter}
61
+ * @throws {Boom.Boom} 503 (serverUnavailable) when SCHEDULER_PROVIDER is unset
62
+ * in a deployed Lambda.
63
+ */
64
+ function createSchedulerAdapterFromEnv() {
65
+ const type =
66
+ process.env.SCHEDULER_PROVIDER ||
67
+ (process.env.AWS_LAMBDA_FUNCTION_NAME ? null : 'local');
68
+ if (!type) {
69
+ throw Boom.serverUnavailable(
70
+ 'SCHEDULER_PROVIDER is not configured. Set it (e.g. "aws") via appDefinition.admin.enableScheduling.'
71
+ );
72
+ }
73
+
74
+ return createSchedulerAdapter({
75
+ type,
76
+ targetLambdaArn: process.env.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN,
77
+ scheduleGroupName: process.env.ADMIN_SCRIPT_SCHEDULE_GROUP,
78
+ roleArn: process.env.SCHEDULER_ROLE_ARN,
79
+ });
80
+ }
81
+
49
82
  module.exports = {
50
83
  createSchedulerAdapter,
84
+ createSchedulerAdapterFromEnv,
51
85
  };
@@ -23,7 +23,7 @@ const {
23
23
  } = require('@friggframework/core/application/commands/admin-script-commands');
24
24
  const { QueuerUtil } = require('@friggframework/core/queues');
25
25
  const {
26
- createSchedulerAdapter,
26
+ createSchedulerAdapterFromEnv,
27
27
  } = require('../../adapters/scheduler-adapter-factory');
28
28
 
29
29
  describe('Admin Script Router', () => {
@@ -76,7 +76,7 @@ describe('Admin Script Router', () => {
76
76
  });
77
77
  createScriptRunner.mockReturnValue(mockRunner);
78
78
  createAdminScriptCommands.mockReturnValue(mockCommands);
79
- createSchedulerAdapter.mockReturnValue(mockSchedulerAdapter);
79
+ createSchedulerAdapterFromEnv.mockReturnValue(mockSchedulerAdapter);
80
80
  QueuerUtil.send = jest.fn().mockResolvedValue({});
81
81
 
82
82
  // Default mock implementations
@@ -1,6 +1,5 @@
1
1
  const express = require('express');
2
2
  const serverless = require('serverless-http');
3
- const Boom = require('@hapi/boom');
4
3
  const { validateAdminApiKey } = require('./admin-auth-middleware');
5
4
  const { createScriptRunner } = require('../application/script-runner');
6
5
  const {
@@ -12,7 +11,7 @@ const {
12
11
  } = require('@friggframework/core/application/commands/admin-script-commands');
13
12
  const { QueuerUtil } = require('@friggframework/core/queues');
14
13
  const {
15
- createSchedulerAdapter,
14
+ createSchedulerAdapterFromEnv,
16
15
  } = require('../adapters/scheduler-adapter-factory');
17
16
  const { bootstrapAdminScripts } = require('./bootstrap');
18
17
  const {
@@ -60,51 +59,6 @@ function buildAudit(req) {
60
59
  };
61
60
  }
62
61
 
63
- /**
64
- * Create schedule use case instances
65
- * @param {ScriptFactory} scriptFactory - Registry injected from the request.
66
- * @private
67
- */
68
- function createScheduleUseCases(scriptFactory) {
69
- const commands = createAdminScriptCommands();
70
-
71
- // The local adapter is in-memory only (schedules vanish on cold start), so it
72
- // must never be the silent default in a deployed Lambda. Require an explicit
73
- // provider when running on AWS; fall back to 'local' only for local dev/tests.
74
- const schedulerType =
75
- process.env.SCHEDULER_PROVIDER ||
76
- (process.env.AWS_LAMBDA_FUNCTION_NAME ? null : 'local');
77
- if (!schedulerType) {
78
- throw Boom.serverUnavailable(
79
- 'SCHEDULER_PROVIDER is not configured. Set it (e.g. "aws") via appDefinition.admin.enableScheduling.'
80
- );
81
- }
82
-
83
- const schedulerAdapter = createSchedulerAdapter({
84
- type: schedulerType,
85
- targetLambdaArn: process.env.ADMIN_SCRIPT_EXECUTOR_LAMBDA_ARN,
86
- scheduleGroupName: process.env.ADMIN_SCRIPT_SCHEDULE_GROUP,
87
- roleArn: process.env.SCHEDULER_ROLE_ARN,
88
- });
89
-
90
- return {
91
- getEffectiveSchedule: new GetEffectiveScheduleUseCase({
92
- commands,
93
- scriptFactory,
94
- }),
95
- upsertSchedule: new UpsertScheduleUseCase({
96
- commands,
97
- schedulerAdapter,
98
- scriptFactory,
99
- }),
100
- deleteSchedule: new DeleteScheduleUseCase({
101
- commands,
102
- schedulerAdapter,
103
- scriptFactory,
104
- }),
105
- };
106
- }
107
-
108
62
  /**
109
63
  * GET /admin/scripts
110
64
  * List all registered scripts
@@ -360,7 +314,11 @@ router.get('/scripts/:scriptName/schedule', async (req, res) => {
360
314
  try {
361
315
  const { scriptName } = req.params;
362
316
  const { scriptFactory } = bootstrapAdminScripts();
363
- const { getEffectiveSchedule } = createScheduleUseCases(scriptFactory);
317
+ const commands = createAdminScriptCommands();
318
+ const getEffectiveSchedule = new GetEffectiveScheduleUseCase({
319
+ commands,
320
+ scriptFactory,
321
+ });
364
322
 
365
323
  const result = await getEffectiveSchedule.execute(scriptName);
366
324
 
@@ -383,7 +341,13 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
383
341
  const { scriptName } = req.params;
384
342
  const { enabled, cronExpression, timezone } = req.body;
385
343
  const { scriptFactory } = bootstrapAdminScripts();
386
- const { upsertSchedule } = createScheduleUseCases(scriptFactory);
344
+ const commands = createAdminScriptCommands();
345
+ const schedulerAdapter = createSchedulerAdapterFromEnv();
346
+ const upsertSchedule = new UpsertScheduleUseCase({
347
+ commands,
348
+ schedulerAdapter,
349
+ scriptFactory,
350
+ });
387
351
 
388
352
  const result = await upsertSchedule.execute(scriptName, {
389
353
  enabled,
@@ -414,7 +378,13 @@ router.delete('/scripts/:scriptName/schedule', async (req, res) => {
414
378
  try {
415
379
  const { scriptName } = req.params;
416
380
  const { scriptFactory } = bootstrapAdminScripts();
417
- const { deleteSchedule } = createScheduleUseCases(scriptFactory);
381
+ const commands = createAdminScriptCommands();
382
+ const schedulerAdapter = createSchedulerAdapterFromEnv();
383
+ const deleteSchedule = new DeleteScheduleUseCase({
384
+ commands,
385
+ schedulerAdapter,
386
+ scriptFactory,
387
+ });
418
388
 
419
389
  const result = await deleteSchedule.execute(scriptName);
420
390