@friggframework/admin-scripts 2.0.0--canary.517.06fe141.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.
- package/README.md +1 -1
- package/package.json +6 -6
- package/src/adapters/__tests__/scheduler-adapter-factory.test.js +51 -1
- package/src/adapters/scheduler-adapter-factory.js +37 -3
- package/src/application/__tests__/admin-script-context.test.js +0 -19
- package/src/application/admin-script-context.js +2 -3
- package/src/infrastructure/__tests__/admin-script-router.test.js +66 -2
- package/src/infrastructure/admin-script-router.js +22 -52
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Typical use cases:
|
|
|
6
6
|
|
|
7
7
|
- **Healing scripts** — repair broken integration state (e.g. corrupted config).
|
|
8
8
|
- **Recurring maintenance** — refresh webhooks/subscriptions before they expire.
|
|
9
|
-
- **
|
|
9
|
+
- **Operational tasks** — OAuth token refresh, integration health checks, one-off data backfills. (You write these — none ship built-in.)
|
|
10
10
|
|
|
11
11
|
> Admin scripts are a **high-privilege** surface. Every endpoint is protected by an admin API key (`x-frigg-admin-api-key`), scripts run in your private VPC subnets, and every execution is tracked in the `AdminScriptExecution` table. Never expose the admin API key to browsers or end users.
|
|
12
12
|
|
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.
|
|
4
|
+
"version": "2.0.0--canary.517.a353e20.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.
|
|
8
|
+
"@friggframework/core": "2.0.0--canary.517.a353e20.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.
|
|
15
|
-
"@friggframework/prettier-config": "2.0.0--canary.517.
|
|
16
|
-
"@friggframework/test": "2.0.0--canary.517.
|
|
14
|
+
"@friggframework/eslint-config": "2.0.0--canary.517.a353e20.0",
|
|
15
|
+
"@friggframework/prettier-config": "2.0.0--canary.517.a353e20.0",
|
|
16
|
+
"@friggframework/test": "2.0.0--canary.517.a353e20.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": "
|
|
47
|
+
"gitHead": "a353e204564567f210a23149f4fa7fdb6d779d72"
|
|
48
48
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
const {
|
|
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
|
-
*
|
|
8
|
+
* Infrastructure Layer - Hexagonal Architecture
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
};
|
|
@@ -111,27 +111,8 @@ describe('AdminScriptContext', () => {
|
|
|
111
111
|
mockFactory.getInstanceFromIntegrationId
|
|
112
112
|
).toHaveBeenCalledWith({
|
|
113
113
|
integrationId: 'int_123',
|
|
114
|
-
_isAdminContext: true,
|
|
115
114
|
});
|
|
116
115
|
});
|
|
117
|
-
|
|
118
|
-
it('passes _isAdminContext: true', async () => {
|
|
119
|
-
const mockInstance = { primary: { api: {} } };
|
|
120
|
-
const mockFactory = {
|
|
121
|
-
getInstanceFromIntegrationId: jest
|
|
122
|
-
.fn()
|
|
123
|
-
.mockResolvedValue(mockInstance),
|
|
124
|
-
};
|
|
125
|
-
const ctx = new AdminScriptContext({
|
|
126
|
-
integrationFactory: mockFactory,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
await ctx.instantiate('int_123');
|
|
130
|
-
|
|
131
|
-
const callArgs =
|
|
132
|
-
mockFactory.getInstanceFromIntegrationId.mock.calls[0][0];
|
|
133
|
-
expect(callArgs._isAdminContext).toBe(true);
|
|
134
|
-
});
|
|
135
116
|
});
|
|
136
117
|
|
|
137
118
|
describe('queueScript()', () => {
|
|
@@ -11,8 +11,8 @@ const { QueuerUtil } = require('@friggframework/core/queues');
|
|
|
11
11
|
* `commands.entities`, and `commands.integrations` expose the framework's
|
|
12
12
|
* command layer. Each command returns data on success or an `{ error }`
|
|
13
13
|
* object on failure — scripts check `.error` themselves.
|
|
14
|
-
* - **
|
|
15
|
-
*
|
|
14
|
+
* - **Integration instantiation**: `instantiate(integrationId)` hydrates a live
|
|
15
|
+
* integration instance (system-scoped load) for calling external APIs
|
|
16
16
|
* - **Script chaining**: `queueScript()` / `queueScriptBatch()` let scripts
|
|
17
17
|
* enqueue follow-up work with parent execution tracking
|
|
18
18
|
* - **Execution-scoped logging**: `log()` collects structured entries tied
|
|
@@ -52,7 +52,6 @@ class AdminScriptContext {
|
|
|
52
52
|
}
|
|
53
53
|
return this.integrationFactory.getInstanceFromIntegrationId({
|
|
54
54
|
integrationId,
|
|
55
|
-
_isAdminContext: true, // Bypass user ownership check
|
|
56
55
|
});
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
79
|
+
createSchedulerAdapterFromEnv.mockReturnValue(mockSchedulerAdapter);
|
|
80
80
|
QueuerUtil.send = jest.fn().mockResolvedValue({});
|
|
81
81
|
|
|
82
82
|
// Default mock implementations
|
|
@@ -253,6 +253,70 @@ describe('Admin Script Router', () => {
|
|
|
253
253
|
expect(response.status).toBe(404);
|
|
254
254
|
expect(response.body.code).toBe('SCRIPT_NOT_FOUND');
|
|
255
255
|
});
|
|
256
|
+
|
|
257
|
+
it('rejects a sync script whose timeout exceeds the API budget on AWS', async () => {
|
|
258
|
+
// TestScript.Definition.config.timeout is 300000 (> 25000)
|
|
259
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME = 'admin-script-router';
|
|
260
|
+
|
|
261
|
+
const response = await request(app)
|
|
262
|
+
.post('/admin/scripts/test-script')
|
|
263
|
+
.send({ params: {}, mode: 'sync' });
|
|
264
|
+
|
|
265
|
+
expect(response.status).toBe(400);
|
|
266
|
+
expect(response.body.code).toBe('SYNC_TIMEOUT_TOO_LONG');
|
|
267
|
+
expect(mockRunner.execute).not.toHaveBeenCalled();
|
|
268
|
+
delete process.env.AWS_LAMBDA_FUNCTION_NAME;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('allows a sync script within the budget on AWS (25000 boundary)', async () => {
|
|
272
|
+
process.env.AWS_LAMBDA_FUNCTION_NAME = 'admin-script-router';
|
|
273
|
+
class ShortScript extends AdminScriptBase {
|
|
274
|
+
static Definition = {
|
|
275
|
+
name: 'short-script',
|
|
276
|
+
version: '1.0.0',
|
|
277
|
+
description: 'within budget',
|
|
278
|
+
config: { timeout: 25000 },
|
|
279
|
+
};
|
|
280
|
+
async execute() {
|
|
281
|
+
return {};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
mockFactory.get.mockReturnValue(ShortScript);
|
|
285
|
+
mockRunner.execute.mockResolvedValue({
|
|
286
|
+
executionId: 'exec-1',
|
|
287
|
+
status: 'COMPLETED',
|
|
288
|
+
scriptName: 'short-script',
|
|
289
|
+
output: {},
|
|
290
|
+
metrics: { durationMs: 1 },
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const response = await request(app)
|
|
294
|
+
.post('/admin/scripts/short-script')
|
|
295
|
+
.send({ params: {}, mode: 'sync' });
|
|
296
|
+
|
|
297
|
+
expect(response.status).toBe(200);
|
|
298
|
+
expect(mockRunner.execute).toHaveBeenCalled();
|
|
299
|
+
delete process.env.AWS_LAMBDA_FUNCTION_NAME;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('does not queue and surfaces the error when createExecution fails (async)', async () => {
|
|
303
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
304
|
+
'https://sqs.us-east-1.amazonaws.com/123/test-queue';
|
|
305
|
+
mockCommands.createExecution.mockResolvedValue({
|
|
306
|
+
error: 500,
|
|
307
|
+
reason: 'DB down',
|
|
308
|
+
code: 'DB_ERROR',
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const response = await request(app)
|
|
312
|
+
.post('/admin/scripts/test-script')
|
|
313
|
+
.send({ params: { foo: 'bar' }, mode: 'async' });
|
|
314
|
+
|
|
315
|
+
expect(response.status).toBe(500);
|
|
316
|
+
expect(response.body.error).toBe('DB down');
|
|
317
|
+
expect(QueuerUtil.send).not.toHaveBeenCalled();
|
|
318
|
+
delete process.env.ADMIN_SCRIPT_QUEUE_URL;
|
|
319
|
+
});
|
|
256
320
|
});
|
|
257
321
|
|
|
258
322
|
describe('GET /admin/scripts/:scriptName/executions/:executionId', () => {
|
|
@@ -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
|
-
|
|
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
|
|
@@ -354,13 +308,17 @@ router.get('/scripts/:scriptName/executions', async (req, res) => {
|
|
|
354
308
|
|
|
355
309
|
/**
|
|
356
310
|
* GET /admin/scripts/:scriptName/schedule
|
|
357
|
-
* Get effective schedule (DB override
|
|
311
|
+
* Get the effective schedule (the DB override, or none)
|
|
358
312
|
*/
|
|
359
313
|
router.get('/scripts/:scriptName/schedule', async (req, res) => {
|
|
360
314
|
try {
|
|
361
315
|
const { scriptName } = req.params;
|
|
362
316
|
const { scriptFactory } = bootstrapAdminScripts();
|
|
363
|
-
const
|
|
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
|
|
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,
|
|
@@ -408,13 +372,19 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
408
372
|
|
|
409
373
|
/**
|
|
410
374
|
* DELETE /admin/scripts/:scriptName/schedule
|
|
411
|
-
* Remove schedule override
|
|
375
|
+
* Remove the schedule override
|
|
412
376
|
*/
|
|
413
377
|
router.delete('/scripts/:scriptName/schedule', async (req, res) => {
|
|
414
378
|
try {
|
|
415
379
|
const { scriptName } = req.params;
|
|
416
380
|
const { scriptFactory } = bootstrapAdminScripts();
|
|
417
|
-
const
|
|
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
|
|