@friggframework/admin-scripts 2.0.0--canary.517.ff03f2c.0 → 2.0.0--canary.517.c197eb5.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 +7 -6
- package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +1 -1
- package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +1 -1
- package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +3 -3
- package/src/application/use-cases/delete-schedule-use-case.js +3 -3
- package/src/application/use-cases/get-effective-schedule-use-case.js +3 -3
- package/src/application/use-cases/upsert-schedule-use-case.js +5 -9
- package/src/infrastructure/__tests__/admin-script-router.test.js +3 -5
- package/src/infrastructure/admin-script-router.js +20 -49
package/package.json
CHANGED
|
@@ -1,18 +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.c197eb5.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.c197eb5.0",
|
|
9
|
+
"@hapi/boom": "^10.0.1",
|
|
9
10
|
"express": "^4.18.2",
|
|
10
11
|
"serverless-http": "^3.2.0"
|
|
11
12
|
},
|
|
12
13
|
"devDependencies": {
|
|
13
|
-
"@friggframework/eslint-config": "2.0.0--canary.517.
|
|
14
|
-
"@friggframework/prettier-config": "2.0.0--canary.517.
|
|
15
|
-
"@friggframework/test": "2.0.0--canary.517.
|
|
14
|
+
"@friggframework/eslint-config": "2.0.0--canary.517.c197eb5.0",
|
|
15
|
+
"@friggframework/prettier-config": "2.0.0--canary.517.c197eb5.0",
|
|
16
|
+
"@friggframework/test": "2.0.0--canary.517.c197eb5.0",
|
|
16
17
|
"eslint": "^8.22.0",
|
|
17
18
|
"jest": "^29.7.0",
|
|
18
19
|
"prettier": "^2.7.1",
|
|
@@ -43,5 +44,5 @@
|
|
|
43
44
|
"maintenance",
|
|
44
45
|
"operations"
|
|
45
46
|
],
|
|
46
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "c197eb5c237e263a8d1b65a94f213ee4732b3c57"
|
|
47
48
|
}
|
|
@@ -153,7 +153,7 @@ describe('UpsertScheduleUseCase', () => {
|
|
|
153
153
|
try {
|
|
154
154
|
await useCase.execute('non-existent', { enabled: true });
|
|
155
155
|
} catch (error) {
|
|
156
|
-
expect(error.
|
|
156
|
+
expect(error.output.statusCode).toBe(404);
|
|
157
157
|
}
|
|
158
158
|
});
|
|
159
159
|
|
|
@@ -167,7 +167,7 @@ describe('UpsertScheduleUseCase', () => {
|
|
|
167
167
|
try {
|
|
168
168
|
await useCase.execute('test-script', { enabled: 'yes' });
|
|
169
169
|
} catch (error) {
|
|
170
|
-
expect(error.
|
|
170
|
+
expect(error.output.statusCode).toBe(400);
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
|
|
@@ -183,7 +183,7 @@ describe('UpsertScheduleUseCase', () => {
|
|
|
183
183
|
try {
|
|
184
184
|
await useCase.execute('test-script', { enabled: true });
|
|
185
185
|
} catch (error) {
|
|
186
|
-
expect(error.
|
|
186
|
+
expect(error.output.statusCode).toBe(400);
|
|
187
187
|
}
|
|
188
188
|
});
|
|
189
189
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const Boom = require('@hapi/boom');
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Delete Schedule Use Case
|
|
3
5
|
*
|
|
@@ -51,9 +53,7 @@ class DeleteScheduleUseCase {
|
|
|
51
53
|
*/
|
|
52
54
|
_validateScriptExists(scriptName) {
|
|
53
55
|
if (!this.scriptFactory.has(scriptName)) {
|
|
54
|
-
|
|
55
|
-
error.code = 'SCRIPT_NOT_FOUND';
|
|
56
|
-
throw error;
|
|
56
|
+
throw Boom.notFound(`Script "${scriptName}" not found`);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const Boom = require('@hapi/boom');
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Get Effective Schedule Use Case
|
|
3
5
|
*
|
|
@@ -62,9 +64,7 @@ class GetEffectiveScheduleUseCase {
|
|
|
62
64
|
*/
|
|
63
65
|
_validateScriptExists(scriptName) {
|
|
64
66
|
if (!this.scriptFactory.has(scriptName)) {
|
|
65
|
-
|
|
66
|
-
error.code = 'SCRIPT_NOT_FOUND';
|
|
67
|
-
throw error;
|
|
67
|
+
throw Boom.notFound(`Script "${scriptName}" not found`);
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const Boom = require('@hapi/boom');
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Upsert Schedule Use Case
|
|
3
5
|
*
|
|
@@ -65,9 +67,7 @@ class UpsertScheduleUseCase {
|
|
|
65
67
|
*/
|
|
66
68
|
_validateScriptExists(scriptName) {
|
|
67
69
|
if (!this.scriptFactory.has(scriptName)) {
|
|
68
|
-
|
|
69
|
-
error.code = 'SCRIPT_NOT_FOUND';
|
|
70
|
-
throw error;
|
|
70
|
+
throw Boom.notFound(`Script "${scriptName}" not found`);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -76,17 +76,13 @@ class UpsertScheduleUseCase {
|
|
|
76
76
|
*/
|
|
77
77
|
_validateInput(enabled, cronExpression) {
|
|
78
78
|
if (typeof enabled !== 'boolean') {
|
|
79
|
-
|
|
80
|
-
error.code = 'INVALID_INPUT';
|
|
81
|
-
throw error;
|
|
79
|
+
throw Boom.badRequest('enabled must be a boolean');
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
if (enabled && !cronExpression) {
|
|
85
|
-
|
|
83
|
+
throw Boom.badRequest(
|
|
86
84
|
'cronExpression is required when enabled is true'
|
|
87
85
|
);
|
|
88
|
-
error.code = 'INVALID_INPUT';
|
|
89
|
-
throw error;
|
|
90
86
|
}
|
|
91
87
|
}
|
|
92
88
|
|
|
@@ -404,7 +404,7 @@ describe('Admin Script Router', () => {
|
|
|
404
404
|
);
|
|
405
405
|
|
|
406
406
|
expect(response.status).toBe(404);
|
|
407
|
-
expect(response.body.
|
|
407
|
+
expect(response.body.error).toMatch(/not found/i);
|
|
408
408
|
});
|
|
409
409
|
});
|
|
410
410
|
|
|
@@ -481,7 +481,6 @@ describe('Admin Script Router', () => {
|
|
|
481
481
|
});
|
|
482
482
|
|
|
483
483
|
expect(response.status).toBe(400);
|
|
484
|
-
expect(response.body.code).toBe('INVALID_INPUT');
|
|
485
484
|
expect(response.body.error).toContain('enabled');
|
|
486
485
|
});
|
|
487
486
|
|
|
@@ -493,7 +492,6 @@ describe('Admin Script Router', () => {
|
|
|
493
492
|
});
|
|
494
493
|
|
|
495
494
|
expect(response.status).toBe(400);
|
|
496
|
-
expect(response.body.code).toBe('INVALID_INPUT');
|
|
497
495
|
expect(response.body.error).toContain('cronExpression');
|
|
498
496
|
});
|
|
499
497
|
|
|
@@ -508,7 +506,7 @@ describe('Admin Script Router', () => {
|
|
|
508
506
|
});
|
|
509
507
|
|
|
510
508
|
expect(response.status).toBe(404);
|
|
511
|
-
expect(response.body.
|
|
509
|
+
expect(response.body.error).toMatch(/not found/i);
|
|
512
510
|
});
|
|
513
511
|
|
|
514
512
|
it('should provision EventBridge schedule when enabled', async () => {
|
|
@@ -712,7 +710,7 @@ describe('Admin Script Router', () => {
|
|
|
712
710
|
);
|
|
713
711
|
|
|
714
712
|
expect(response.status).toBe(404);
|
|
715
|
-
expect(response.body.
|
|
713
|
+
expect(response.body.error).toMatch(/not found/i);
|
|
716
714
|
});
|
|
717
715
|
|
|
718
716
|
it('should delete EventBridge schedule when external rule exists', async () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const serverless = require('serverless-http');
|
|
3
|
+
const Boom = require('@hapi/boom');
|
|
3
4
|
const { validateAdminApiKey } = require('./admin-auth-middleware');
|
|
4
5
|
const { getScriptFactory } = require('../application/script-factory');
|
|
5
6
|
const { createScriptRunner } = require('../application/script-runner');
|
|
@@ -26,13 +27,27 @@ const router = express.Router();
|
|
|
26
27
|
// Apply auth middleware to all admin routes
|
|
27
28
|
router.use(validateAdminApiKey);
|
|
28
29
|
|
|
29
|
-
// Register the host app's admin scripts
|
|
30
|
+
// Register the host app's admin scripts before handling requests.
|
|
30
31
|
// Memoized, so this only does work on the first request per process.
|
|
31
32
|
router.use((_req, _res, next) => {
|
|
32
33
|
bootstrapAdminScripts();
|
|
33
34
|
next();
|
|
34
35
|
});
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Translate a thrown error into an HTTP response. Boom errors (thrown by the
|
|
39
|
+
* schedule use cases) carry their own status code; anything else is an
|
|
40
|
+
* unexpected 500. Mirrors the framework's app-handler-helpers convention.
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
function sendError(res, error, fallbackMessage) {
|
|
44
|
+
if (error.isBoom) {
|
|
45
|
+
return res.status(error.output.statusCode).json({ error: error.message });
|
|
46
|
+
}
|
|
47
|
+
console.error(fallbackMessage, error);
|
|
48
|
+
return res.status(500).json({ error: fallbackMessage });
|
|
49
|
+
}
|
|
50
|
+
|
|
36
51
|
/**
|
|
37
52
|
* Build audit metadata for an execution from the request.
|
|
38
53
|
* @private
|
|
@@ -62,11 +77,9 @@ function createScheduleUseCases() {
|
|
|
62
77
|
process.env.SCHEDULER_PROVIDER ||
|
|
63
78
|
(process.env.AWS_LAMBDA_FUNCTION_NAME ? null : 'local');
|
|
64
79
|
if (!schedulerType) {
|
|
65
|
-
|
|
80
|
+
throw Boom.serverUnavailable(
|
|
66
81
|
'SCHEDULER_PROVIDER is not configured. Set it (e.g. "aws") via appDefinition.admin.enableScheduling.'
|
|
67
82
|
);
|
|
68
|
-
error.code = 'SCHEDULER_NOT_CONFIGURED';
|
|
69
|
-
throw error;
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
const schedulerAdapter = createSchedulerAdapter({
|
|
@@ -354,19 +367,7 @@ router.get('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
354
367
|
...result.schedule,
|
|
355
368
|
});
|
|
356
369
|
} catch (error) {
|
|
357
|
-
|
|
358
|
-
return res.status(404).json({
|
|
359
|
-
error: error.message,
|
|
360
|
-
code: error.code,
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
if (error.code === 'SCHEDULER_NOT_CONFIGURED') {
|
|
364
|
-
return res
|
|
365
|
-
.status(503)
|
|
366
|
-
.json({ error: error.message, code: error.code });
|
|
367
|
-
}
|
|
368
|
-
console.error('Error getting schedule:', error);
|
|
369
|
-
res.status(500).json({ error: 'Failed to get schedule' });
|
|
370
|
+
return sendError(res, error, 'Failed to get schedule');
|
|
370
371
|
}
|
|
371
372
|
});
|
|
372
373
|
|
|
@@ -397,25 +398,7 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
397
398
|
}),
|
|
398
399
|
});
|
|
399
400
|
} catch (error) {
|
|
400
|
-
|
|
401
|
-
return res.status(404).json({
|
|
402
|
-
error: error.message,
|
|
403
|
-
code: error.code,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
if (error.code === 'INVALID_INPUT') {
|
|
407
|
-
return res.status(400).json({
|
|
408
|
-
error: error.message,
|
|
409
|
-
code: error.code,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
if (error.code === 'SCHEDULER_NOT_CONFIGURED') {
|
|
413
|
-
return res
|
|
414
|
-
.status(503)
|
|
415
|
-
.json({ error: error.message, code: error.code });
|
|
416
|
-
}
|
|
417
|
-
console.error('Error updating schedule:', error);
|
|
418
|
-
res.status(500).json({ error: 'Failed to update schedule' });
|
|
401
|
+
return sendError(res, error, 'Failed to update schedule');
|
|
419
402
|
}
|
|
420
403
|
});
|
|
421
404
|
|
|
@@ -432,19 +415,7 @@ router.delete('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
432
415
|
|
|
433
416
|
res.json(result);
|
|
434
417
|
} catch (error) {
|
|
435
|
-
|
|
436
|
-
return res.status(404).json({
|
|
437
|
-
error: error.message,
|
|
438
|
-
code: error.code,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
if (error.code === 'SCHEDULER_NOT_CONFIGURED') {
|
|
442
|
-
return res
|
|
443
|
-
.status(503)
|
|
444
|
-
.json({ error: error.message, code: error.code });
|
|
445
|
-
}
|
|
446
|
-
console.error('Error deleting schedule:', error);
|
|
447
|
-
res.status(500).json({ error: 'Failed to delete schedule' });
|
|
418
|
+
return sendError(res, error, 'Failed to delete schedule');
|
|
448
419
|
}
|
|
449
420
|
});
|
|
450
421
|
|