@friggframework/admin-scripts 2.0.0--canary.545.b4ca16d.0 → 2.0.0--canary.517.8eaf5df.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 +277 -0
- package/index.js +23 -6
- package/package.json +6 -6
- package/src/adapters/__tests__/aws-scheduler-adapter.test.js +106 -45
- package/src/adapters/__tests__/local-scheduler-adapter.test.js +24 -10
- package/src/adapters/__tests__/scheduler-adapter-factory.test.js +30 -9
- package/src/adapters/__tests__/scheduler-adapter.test.js +6 -2
- package/src/adapters/aws-scheduler-adapter.js +55 -28
- package/src/adapters/local-scheduler-adapter.js +2 -2
- package/src/adapters/scheduler-adapter-factory.js +3 -1
- package/src/adapters/scheduler-adapter.js +9 -3
- package/src/application/__tests__/admin-frigg-commands.test.js +73 -30
- package/src/application/__tests__/admin-script-base.test.js +23 -6
- package/src/application/__tests__/script-factory.test.js +30 -6
- package/src/application/__tests__/script-runner.test.js +113 -24
- package/src/application/__tests__/validate-script-input.test.js +54 -15
- package/src/application/admin-frigg-commands.js +21 -9
- package/src/application/admin-script-base.js +3 -2
- package/src/application/script-factory.js +3 -1
- package/src/application/script-runner.js +90 -48
- package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +16 -7
- package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +9 -4
- package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +21 -10
- package/src/application/use-cases/delete-schedule-use-case.js +6 -4
- package/src/application/use-cases/get-effective-schedule-use-case.js +3 -1
- package/src/application/use-cases/index.js +3 -1
- package/src/application/use-cases/upsert-schedule-use-case.js +30 -11
- package/src/application/validate-script-input.js +10 -3
- package/src/builtins/__tests__/integration-health-check.test.js +232 -127
- package/src/builtins/__tests__/oauth-token-refresh.test.js +128 -75
- package/src/builtins/index.js +1 -4
- package/src/builtins/integration-health-check.js +63 -30
- package/src/builtins/oauth-token-refresh.js +64 -29
- package/src/infrastructure/__tests__/admin-auth-middleware.test.js +9 -3
- package/src/infrastructure/__tests__/admin-script-router.test.js +125 -52
- package/src/infrastructure/admin-auth-middleware.js +3 -1
- package/src/infrastructure/admin-script-router.js +129 -14
- package/src/infrastructure/bootstrap.js +87 -0
- package/src/infrastructure/script-executor-handler.js +119 -52
- package/src/application/__tests__/dry-run-http-interceptor.test.js +0 -313
- package/src/application/__tests__/dry-run-repository-wrapper.test.js +0 -257
- package/src/application/__tests__/schedule-management-use-case.test.js +0 -276
- package/src/application/dry-run-http-interceptor.js +0 -296
- package/src/application/dry-run-repository-wrapper.js +0 -261
- package/src/application/schedule-management-use-case.js +0 -230
|
@@ -32,7 +32,7 @@ describe('LocalSchedulerAdapter', () => {
|
|
|
32
32
|
const result = await adapter.createSchedule(config);
|
|
33
33
|
|
|
34
34
|
expect(result).toEqual({
|
|
35
|
-
scheduleName: 'test-script',
|
|
35
|
+
scheduleName: 'frigg-script-test-script',
|
|
36
36
|
scheduleArn: 'local:schedule:test-script',
|
|
37
37
|
});
|
|
38
38
|
expect(adapter.size).toBe(1);
|
|
@@ -49,13 +49,17 @@ describe('LocalSchedulerAdapter', () => {
|
|
|
49
49
|
const result = await adapter.createSchedule(config);
|
|
50
50
|
|
|
51
51
|
expect(result).toEqual({
|
|
52
|
-
scheduleName: 'test-script',
|
|
52
|
+
scheduleName: 'frigg-script-test-script',
|
|
53
53
|
scheduleArn: 'local:schedule:test-script',
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
const schedule = await adapter.getSchedule('test-script');
|
|
57
|
-
expect(schedule.ScheduleExpressionTimezone).toBe(
|
|
58
|
-
|
|
57
|
+
expect(schedule.ScheduleExpressionTimezone).toBe(
|
|
58
|
+
'America/New_York'
|
|
59
|
+
);
|
|
60
|
+
expect(JSON.parse(schedule.Target.Input).params).toEqual({
|
|
61
|
+
key: 'value',
|
|
62
|
+
});
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
it('should default timezone to UTC', async () => {
|
|
@@ -119,7 +123,9 @@ describe('LocalSchedulerAdapter', () => {
|
|
|
119
123
|
});
|
|
120
124
|
|
|
121
125
|
it('should not throw error when deleting non-existent schedule', async () => {
|
|
122
|
-
await expect(
|
|
126
|
+
await expect(
|
|
127
|
+
adapter.deleteSchedule('non-existent')
|
|
128
|
+
).resolves.toBeUndefined();
|
|
123
129
|
});
|
|
124
130
|
|
|
125
131
|
it('should clear intervals if they exist', async () => {
|
|
@@ -210,9 +216,15 @@ describe('LocalSchedulerAdapter', () => {
|
|
|
210
216
|
const schedules = await adapter.listSchedules();
|
|
211
217
|
|
|
212
218
|
expect(schedules).toHaveLength(3);
|
|
213
|
-
expect(schedules.map((s) => s.Name)).toContain(
|
|
214
|
-
|
|
215
|
-
|
|
219
|
+
expect(schedules.map((s) => s.Name)).toContain(
|
|
220
|
+
'frigg-script-script-1'
|
|
221
|
+
);
|
|
222
|
+
expect(schedules.map((s) => s.Name)).toContain(
|
|
223
|
+
'frigg-script-script-2'
|
|
224
|
+
);
|
|
225
|
+
expect(schedules.map((s) => s.Name)).toContain(
|
|
226
|
+
'frigg-script-script-3'
|
|
227
|
+
);
|
|
216
228
|
});
|
|
217
229
|
|
|
218
230
|
it('should include all schedule properties in normalized format', async () => {
|
|
@@ -247,10 +259,12 @@ describe('LocalSchedulerAdapter', () => {
|
|
|
247
259
|
it('should return schedule details', async () => {
|
|
248
260
|
const schedule = await adapter.getSchedule('test-script');
|
|
249
261
|
|
|
250
|
-
expect(schedule.Name).toBe('test-script');
|
|
262
|
+
expect(schedule.Name).toBe('frigg-script-test-script');
|
|
251
263
|
expect(schedule.State).toBe('ENABLED');
|
|
252
264
|
expect(schedule.ScheduleExpression).toBe('0 0 * * *');
|
|
253
|
-
expect(schedule.ScheduleExpressionTimezone).toBe(
|
|
265
|
+
expect(schedule.ScheduleExpressionTimezone).toBe(
|
|
266
|
+
'America/New_York'
|
|
267
|
+
);
|
|
254
268
|
});
|
|
255
269
|
|
|
256
270
|
it('should include target configuration', async () => {
|
|
@@ -48,22 +48,34 @@ describe('Scheduler Adapter Factory', () => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('should create AWS adapter when type is "aws"', () => {
|
|
51
|
-
const adapter = createSchedulerAdapter({
|
|
51
|
+
const adapter = createSchedulerAdapter({
|
|
52
|
+
type: 'aws',
|
|
53
|
+
...awsAdapterParams,
|
|
54
|
+
});
|
|
52
55
|
|
|
53
56
|
expect(adapter).toBeInstanceOf(AWSSchedulerAdapter);
|
|
54
57
|
expect(adapter.getName()).toBe('aws-eventbridge-scheduler');
|
|
55
58
|
});
|
|
56
59
|
|
|
57
60
|
it('should create AWS adapter when type is "eventbridge"', () => {
|
|
58
|
-
const adapter = createSchedulerAdapter({
|
|
61
|
+
const adapter = createSchedulerAdapter({
|
|
62
|
+
type: 'eventbridge',
|
|
63
|
+
...awsAdapterParams,
|
|
64
|
+
});
|
|
59
65
|
|
|
60
66
|
expect(adapter).toBeInstanceOf(AWSSchedulerAdapter);
|
|
61
67
|
});
|
|
62
68
|
|
|
63
69
|
it('should handle case-insensitive type values', () => {
|
|
64
|
-
const adapter1 = createSchedulerAdapter({
|
|
70
|
+
const adapter1 = createSchedulerAdapter({
|
|
71
|
+
type: 'AWS',
|
|
72
|
+
...awsAdapterParams,
|
|
73
|
+
});
|
|
65
74
|
const adapter2 = createSchedulerAdapter({ type: 'LOCAL' });
|
|
66
|
-
const adapter3 = createSchedulerAdapter({
|
|
75
|
+
const adapter3 = createSchedulerAdapter({
|
|
76
|
+
type: 'EventBridge',
|
|
77
|
+
...awsAdapterParams,
|
|
78
|
+
});
|
|
67
79
|
|
|
68
80
|
expect(adapter1).toBeInstanceOf(AWSSchedulerAdapter);
|
|
69
81
|
expect(adapter2).toBeInstanceOf(LocalSchedulerAdapter);
|
|
@@ -73,7 +85,8 @@ describe('Scheduler Adapter Factory', () => {
|
|
|
73
85
|
it('should pass AWS configuration to AWS adapter', () => {
|
|
74
86
|
const config = {
|
|
75
87
|
type: 'aws',
|
|
76
|
-
targetLambdaArn:
|
|
88
|
+
targetLambdaArn:
|
|
89
|
+
'arn:aws:lambda:eu-west-1:123456789012:function:test',
|
|
77
90
|
scheduleGroupName: 'custom-group',
|
|
78
91
|
roleArn: 'arn:aws:iam::123456789012:role/custom-role',
|
|
79
92
|
};
|
|
@@ -82,9 +95,13 @@ describe('Scheduler Adapter Factory', () => {
|
|
|
82
95
|
|
|
83
96
|
expect(adapter).toBeInstanceOf(AWSSchedulerAdapter);
|
|
84
97
|
expect(adapter.region).toBe('us-east-1'); // From process.env.AWS_REGION
|
|
85
|
-
expect(adapter.targetLambdaArn).toBe(
|
|
98
|
+
expect(adapter.targetLambdaArn).toBe(
|
|
99
|
+
'arn:aws:lambda:eu-west-1:123456789012:function:test'
|
|
100
|
+
);
|
|
86
101
|
expect(adapter.scheduleGroupName).toBe('custom-group');
|
|
87
|
-
expect(adapter.roleArn).toBe(
|
|
102
|
+
expect(adapter.roleArn).toBe(
|
|
103
|
+
'arn:aws:iam::123456789012:role/custom-role'
|
|
104
|
+
);
|
|
88
105
|
});
|
|
89
106
|
|
|
90
107
|
it('should pass roleArn through to AWS adapter', () => {
|
|
@@ -95,7 +112,9 @@ describe('Scheduler Adapter Factory', () => {
|
|
|
95
112
|
});
|
|
96
113
|
|
|
97
114
|
expect(adapter).toBeInstanceOf(AWSSchedulerAdapter);
|
|
98
|
-
expect(adapter.roleArn).toBe(
|
|
115
|
+
expect(adapter.roleArn).toBe(
|
|
116
|
+
'arn:aws:iam::999999999999:role/scheduler-role'
|
|
117
|
+
);
|
|
99
118
|
});
|
|
100
119
|
|
|
101
120
|
it('should ignore AWS config for local adapter', () => {
|
|
@@ -111,7 +130,9 @@ describe('Scheduler Adapter Factory', () => {
|
|
|
111
130
|
});
|
|
112
131
|
|
|
113
132
|
it('should throw for unknown adapter type', () => {
|
|
114
|
-
expect(() =>
|
|
133
|
+
expect(() =>
|
|
134
|
+
createSchedulerAdapter({ type: 'unknown-type' })
|
|
135
|
+
).toThrow();
|
|
115
136
|
});
|
|
116
137
|
});
|
|
117
138
|
});
|
|
@@ -27,7 +27,9 @@ describe('SchedulerAdapter', () => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
it('should throw error for setScheduleEnabled()', async () => {
|
|
30
|
-
await expect(
|
|
30
|
+
await expect(
|
|
31
|
+
adapter.setScheduleEnabled('test', true)
|
|
32
|
+
).rejects.toThrow(
|
|
31
33
|
'SchedulerAdapter.setScheduleEnabled() must be implemented'
|
|
32
34
|
);
|
|
33
35
|
});
|
|
@@ -95,7 +97,9 @@ describe('SchedulerAdapter', () => {
|
|
|
95
97
|
// Should throw for missing methods
|
|
96
98
|
await expect(incomplete.createSchedule({})).rejects.toThrow();
|
|
97
99
|
await expect(incomplete.deleteSchedule('test')).rejects.toThrow();
|
|
98
|
-
await expect(
|
|
100
|
+
await expect(
|
|
101
|
+
incomplete.setScheduleEnabled('test', true)
|
|
102
|
+
).rejects.toThrow();
|
|
99
103
|
await expect(incomplete.listSchedules()).rejects.toThrow();
|
|
100
104
|
await expect(incomplete.getSchedule('test')).rejects.toThrow();
|
|
101
105
|
});
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const { SchedulerAdapter } = require('./scheduler-adapter');
|
|
2
2
|
|
|
3
3
|
// Lazy-loaded AWS SDK clients (following AWSProviderAdapter pattern)
|
|
4
|
-
let SchedulerClient,
|
|
5
|
-
|
|
4
|
+
let SchedulerClient,
|
|
5
|
+
CreateScheduleCommand,
|
|
6
|
+
DeleteScheduleCommand,
|
|
7
|
+
GetScheduleCommand,
|
|
8
|
+
UpdateScheduleCommand,
|
|
9
|
+
ListSchedulesCommand;
|
|
6
10
|
|
|
7
11
|
function loadSchedulerSDK() {
|
|
8
12
|
if (!SchedulerClient) {
|
|
@@ -25,14 +29,24 @@ function loadSchedulerSDK() {
|
|
|
25
29
|
* Supports cron expressions, timezone configuration, and Lambda invocation.
|
|
26
30
|
*/
|
|
27
31
|
class AWSSchedulerAdapter extends SchedulerAdapter {
|
|
28
|
-
constructor({
|
|
32
|
+
constructor({
|
|
33
|
+
credentials,
|
|
34
|
+
targetLambdaArn,
|
|
35
|
+
scheduleGroupName,
|
|
36
|
+
roleArn,
|
|
37
|
+
} = {}) {
|
|
29
38
|
super();
|
|
30
|
-
if (!targetLambdaArn)
|
|
31
|
-
|
|
39
|
+
if (!targetLambdaArn)
|
|
40
|
+
throw new Error('AWSSchedulerAdapter requires targetLambdaArn');
|
|
41
|
+
if (!scheduleGroupName)
|
|
42
|
+
throw new Error('AWSSchedulerAdapter requires scheduleGroupName');
|
|
32
43
|
if (!roleArn) throw new Error('AWSSchedulerAdapter requires roleArn');
|
|
33
44
|
// Region inherits from the service (set by Lambda runtime, same for all AWS resources)
|
|
34
45
|
const region = process.env.AWS_REGION;
|
|
35
|
-
if (!region)
|
|
46
|
+
if (!region)
|
|
47
|
+
throw new Error(
|
|
48
|
+
'AWSSchedulerAdapter requires AWS_REGION environment variable'
|
|
49
|
+
);
|
|
36
50
|
this.region = region;
|
|
37
51
|
this.credentials = credentials;
|
|
38
52
|
this.targetLambdaArn = targetLambdaArn;
|
|
@@ -79,14 +93,18 @@ class AWSSchedulerAdapter extends SchedulerAdapter {
|
|
|
79
93
|
};
|
|
80
94
|
|
|
81
95
|
try {
|
|
82
|
-
const response = await client.send(
|
|
96
|
+
const response = await client.send(
|
|
97
|
+
new CreateScheduleCommand(scheduleParams)
|
|
98
|
+
);
|
|
83
99
|
return {
|
|
84
100
|
scheduleArn: response.ScheduleArn,
|
|
85
101
|
scheduleName: scheduleName,
|
|
86
102
|
};
|
|
87
103
|
} catch (error) {
|
|
88
104
|
if (error.name === 'ConflictException') {
|
|
89
|
-
const response = await client.send(
|
|
105
|
+
const response = await client.send(
|
|
106
|
+
new UpdateScheduleCommand(scheduleParams)
|
|
107
|
+
);
|
|
90
108
|
return {
|
|
91
109
|
scheduleArn: response.ScheduleArn,
|
|
92
110
|
scheduleName: scheduleName,
|
|
@@ -100,10 +118,12 @@ class AWSSchedulerAdapter extends SchedulerAdapter {
|
|
|
100
118
|
const client = this.getSchedulerClient();
|
|
101
119
|
const scheduleName = `frigg-script-${scriptName}`;
|
|
102
120
|
|
|
103
|
-
await client.send(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
await client.send(
|
|
122
|
+
new DeleteScheduleCommand({
|
|
123
|
+
Name: scheduleName,
|
|
124
|
+
GroupName: this.scheduleGroupName,
|
|
125
|
+
})
|
|
126
|
+
);
|
|
107
127
|
}
|
|
108
128
|
|
|
109
129
|
async setScheduleEnabled(scriptName, enabled) {
|
|
@@ -119,23 +139,28 @@ class AWSSchedulerAdapter extends SchedulerAdapter {
|
|
|
119
139
|
const currentSchedule = await client.send(getCommand);
|
|
120
140
|
|
|
121
141
|
// Update with the new state
|
|
122
|
-
await client.send(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
await client.send(
|
|
143
|
+
new UpdateScheduleCommand({
|
|
144
|
+
Name: scheduleName,
|
|
145
|
+
GroupName: this.scheduleGroupName,
|
|
146
|
+
ScheduleExpression: currentSchedule.ScheduleExpression,
|
|
147
|
+
ScheduleExpressionTimezone:
|
|
148
|
+
currentSchedule.ScheduleExpressionTimezone,
|
|
149
|
+
FlexibleTimeWindow: currentSchedule.FlexibleTimeWindow,
|
|
150
|
+
Target: currentSchedule.Target,
|
|
151
|
+
State: enabled ? 'ENABLED' : 'DISABLED',
|
|
152
|
+
})
|
|
153
|
+
);
|
|
131
154
|
}
|
|
132
155
|
|
|
133
156
|
async listSchedules() {
|
|
134
157
|
const client = this.getSchedulerClient();
|
|
135
158
|
|
|
136
|
-
const response = await client.send(
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
const response = await client.send(
|
|
160
|
+
new ListSchedulesCommand({
|
|
161
|
+
GroupName: this.scheduleGroupName,
|
|
162
|
+
})
|
|
163
|
+
);
|
|
139
164
|
|
|
140
165
|
return response.Schedules || [];
|
|
141
166
|
}
|
|
@@ -144,10 +169,12 @@ class AWSSchedulerAdapter extends SchedulerAdapter {
|
|
|
144
169
|
const client = this.getSchedulerClient();
|
|
145
170
|
const scheduleName = `frigg-script-${scriptName}`;
|
|
146
171
|
|
|
147
|
-
const response = await client.send(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
172
|
+
const response = await client.send(
|
|
173
|
+
new GetScheduleCommand({
|
|
174
|
+
Name: scheduleName,
|
|
175
|
+
GroupName: this.scheduleGroupName,
|
|
176
|
+
})
|
|
177
|
+
);
|
|
151
178
|
|
|
152
179
|
return response;
|
|
153
180
|
}
|
|
@@ -33,7 +33,7 @@ class LocalSchedulerAdapter extends SchedulerAdapter {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
return {
|
|
36
|
-
scheduleName: scriptName
|
|
36
|
+
scheduleName: `frigg-script-${scriptName}`,
|
|
37
37
|
scheduleArn: `local:schedule:${scriptName}`,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -72,7 +72,7 @@ class LocalSchedulerAdapter extends SchedulerAdapter {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return {
|
|
75
|
-
Name: scriptName
|
|
75
|
+
Name: `frigg-script-${scriptName}`,
|
|
76
76
|
State: schedule.enabled ? 'ENABLED' : 'DISABLED',
|
|
77
77
|
ScheduleExpression: schedule.cronExpression,
|
|
78
78
|
ScheduleExpressionTimezone: schedule.timezone,
|
|
@@ -23,7 +23,9 @@ const { LocalSchedulerAdapter } = require('./local-scheduler-adapter');
|
|
|
23
23
|
*/
|
|
24
24
|
function createSchedulerAdapter(options = {}) {
|
|
25
25
|
if (!options.type) {
|
|
26
|
-
throw new Error(
|
|
26
|
+
throw new Error(
|
|
27
|
+
'Scheduler adapter type is required. Configure in appDefinition.adminScripts.scheduler.type'
|
|
28
|
+
);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
switch (options.type.toLowerCase()) {
|
|
@@ -21,7 +21,9 @@ class SchedulerAdapter {
|
|
|
21
21
|
* @returns {Promise<Object>} Created schedule { scheduleArn, scheduleName }
|
|
22
22
|
*/
|
|
23
23
|
async createSchedule(config) {
|
|
24
|
-
throw new Error(
|
|
24
|
+
throw new Error(
|
|
25
|
+
'SchedulerAdapter.createSchedule() must be implemented'
|
|
26
|
+
);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -30,7 +32,9 @@ class SchedulerAdapter {
|
|
|
30
32
|
* @returns {Promise<void>}
|
|
31
33
|
*/
|
|
32
34
|
async deleteSchedule(scriptName) {
|
|
33
|
-
throw new Error(
|
|
35
|
+
throw new Error(
|
|
36
|
+
'SchedulerAdapter.deleteSchedule() must be implemented'
|
|
37
|
+
);
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
/**
|
|
@@ -40,7 +44,9 @@ class SchedulerAdapter {
|
|
|
40
44
|
* @returns {Promise<void>}
|
|
41
45
|
*/
|
|
42
46
|
async setScheduleEnabled(scriptName, enabled) {
|
|
43
|
-
throw new Error(
|
|
47
|
+
throw new Error(
|
|
48
|
+
'SchedulerAdapter.setScheduleEnabled() must be implemented'
|
|
49
|
+
);
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
/**
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
AdminFriggCommands,
|
|
3
|
+
createAdminFriggCommands,
|
|
4
|
+
} = require('../admin-frigg-commands');
|
|
2
5
|
|
|
3
6
|
// Mock all repository factories
|
|
4
|
-
jest.mock(
|
|
7
|
+
jest.mock(
|
|
8
|
+
'@friggframework/core/integrations/repositories/integration-repository-factory'
|
|
9
|
+
);
|
|
5
10
|
jest.mock('@friggframework/core/user/repositories/user-repository-factory');
|
|
6
|
-
jest.mock(
|
|
7
|
-
|
|
11
|
+
jest.mock(
|
|
12
|
+
'@friggframework/core/modules/repositories/module-repository-factory'
|
|
13
|
+
);
|
|
14
|
+
jest.mock(
|
|
15
|
+
'@friggframework/core/credential/repositories/credential-repository-factory'
|
|
16
|
+
);
|
|
8
17
|
jest.mock('@friggframework/core/queues');
|
|
9
18
|
|
|
10
19
|
describe('AdminScriptContext', () => {
|
|
@@ -47,10 +56,18 @@ describe('AdminScriptContext', () => {
|
|
|
47
56
|
batchSend: jest.fn().mockResolvedValue(undefined),
|
|
48
57
|
};
|
|
49
58
|
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const {
|
|
59
|
+
const {
|
|
60
|
+
createIntegrationRepository,
|
|
61
|
+
} = require('@friggframework/core/integrations/repositories/integration-repository-factory');
|
|
62
|
+
const {
|
|
63
|
+
createUserRepository,
|
|
64
|
+
} = require('@friggframework/core/user/repositories/user-repository-factory');
|
|
65
|
+
const {
|
|
66
|
+
createModuleRepository,
|
|
67
|
+
} = require('@friggframework/core/modules/repositories/module-repository-factory');
|
|
68
|
+
const {
|
|
69
|
+
createCredentialRepository,
|
|
70
|
+
} = require('@friggframework/core/credential/repositories/credential-repository-factory');
|
|
54
71
|
const { QueuerUtil } = require('@friggframework/core/queues');
|
|
55
72
|
|
|
56
73
|
createIntegrationRepository.mockReturnValue(mockIntegrationRepo);
|
|
@@ -73,7 +90,9 @@ describe('AdminScriptContext', () => {
|
|
|
73
90
|
|
|
74
91
|
it('creates with integrationFactory', () => {
|
|
75
92
|
const mockFactory = { getInstanceFromIntegrationId: jest.fn() };
|
|
76
|
-
const ctx = new AdminFriggCommands({
|
|
93
|
+
const ctx = new AdminFriggCommands({
|
|
94
|
+
integrationFactory: mockFactory,
|
|
95
|
+
});
|
|
77
96
|
|
|
78
97
|
expect(ctx.integrationFactory).toBe(mockFactory);
|
|
79
98
|
});
|
|
@@ -90,7 +109,9 @@ describe('AdminScriptContext', () => {
|
|
|
90
109
|
describe('Lazy Repository Loading', () => {
|
|
91
110
|
it('creates integrationRepository on first access', () => {
|
|
92
111
|
const ctx = new AdminFriggCommands();
|
|
93
|
-
const {
|
|
112
|
+
const {
|
|
113
|
+
createIntegrationRepository,
|
|
114
|
+
} = require('@friggframework/core/integrations/repositories/integration-repository-factory');
|
|
94
115
|
|
|
95
116
|
expect(createIntegrationRepository).not.toHaveBeenCalled();
|
|
96
117
|
|
|
@@ -112,7 +133,9 @@ describe('AdminScriptContext', () => {
|
|
|
112
133
|
|
|
113
134
|
it('creates userRepository on first access', () => {
|
|
114
135
|
const ctx = new AdminFriggCommands();
|
|
115
|
-
const {
|
|
136
|
+
const {
|
|
137
|
+
createUserRepository,
|
|
138
|
+
} = require('@friggframework/core/user/repositories/user-repository-factory');
|
|
116
139
|
|
|
117
140
|
expect(createUserRepository).not.toHaveBeenCalled();
|
|
118
141
|
|
|
@@ -124,7 +147,9 @@ describe('AdminScriptContext', () => {
|
|
|
124
147
|
|
|
125
148
|
it('creates moduleRepository on first access', () => {
|
|
126
149
|
const ctx = new AdminFriggCommands();
|
|
127
|
-
const {
|
|
150
|
+
const {
|
|
151
|
+
createModuleRepository,
|
|
152
|
+
} = require('@friggframework/core/modules/repositories/module-repository-factory');
|
|
128
153
|
|
|
129
154
|
expect(createModuleRepository).not.toHaveBeenCalled();
|
|
130
155
|
|
|
@@ -136,7 +161,9 @@ describe('AdminScriptContext', () => {
|
|
|
136
161
|
|
|
137
162
|
it('creates credentialRepository on first access', () => {
|
|
138
163
|
const ctx = new AdminFriggCommands();
|
|
139
|
-
const {
|
|
164
|
+
const {
|
|
165
|
+
createCredentialRepository,
|
|
166
|
+
} = require('@friggframework/core/credential/repositories/credential-repository-factory');
|
|
140
167
|
|
|
141
168
|
expect(createCredentialRepository).not.toHaveBeenCalled();
|
|
142
169
|
|
|
@@ -153,21 +180,27 @@ describe('AdminScriptContext', () => {
|
|
|
153
180
|
|
|
154
181
|
await expect(ctx.instantiate('int_123')).rejects.toThrow(
|
|
155
182
|
'instantiate() requires integrationFactory. ' +
|
|
156
|
-
|
|
183
|
+
'Set Definition.config.requireIntegrationInstance = true'
|
|
157
184
|
);
|
|
158
185
|
});
|
|
159
186
|
|
|
160
187
|
it('calls integrationFactory.getInstanceFromIntegrationId', async () => {
|
|
161
188
|
const mockInstance = { primary: { api: {} } };
|
|
162
189
|
const mockFactory = {
|
|
163
|
-
getInstanceFromIntegrationId: jest
|
|
190
|
+
getInstanceFromIntegrationId: jest
|
|
191
|
+
.fn()
|
|
192
|
+
.mockResolvedValue(mockInstance),
|
|
164
193
|
};
|
|
165
|
-
const ctx = new AdminFriggCommands({
|
|
194
|
+
const ctx = new AdminFriggCommands({
|
|
195
|
+
integrationFactory: mockFactory,
|
|
196
|
+
});
|
|
166
197
|
|
|
167
198
|
const result = await ctx.instantiate('int_123');
|
|
168
199
|
|
|
169
200
|
expect(result).toEqual(mockInstance);
|
|
170
|
-
expect(
|
|
201
|
+
expect(
|
|
202
|
+
mockFactory.getInstanceFromIntegrationId
|
|
203
|
+
).toHaveBeenCalledWith({
|
|
171
204
|
integrationId: 'int_123',
|
|
172
205
|
_isAdminContext: true,
|
|
173
206
|
});
|
|
@@ -176,13 +209,18 @@ describe('AdminScriptContext', () => {
|
|
|
176
209
|
it('passes _isAdminContext: true', async () => {
|
|
177
210
|
const mockInstance = { primary: { api: {} } };
|
|
178
211
|
const mockFactory = {
|
|
179
|
-
getInstanceFromIntegrationId: jest
|
|
212
|
+
getInstanceFromIntegrationId: jest
|
|
213
|
+
.fn()
|
|
214
|
+
.mockResolvedValue(mockInstance),
|
|
180
215
|
};
|
|
181
|
-
const ctx = new AdminFriggCommands({
|
|
216
|
+
const ctx = new AdminFriggCommands({
|
|
217
|
+
integrationFactory: mockFactory,
|
|
218
|
+
});
|
|
182
219
|
|
|
183
220
|
await ctx.instantiate('int_123');
|
|
184
221
|
|
|
185
|
-
const callArgs =
|
|
222
|
+
const callArgs =
|
|
223
|
+
mockFactory.getInstanceFromIntegrationId.mock.calls[0][0];
|
|
186
224
|
expect(callArgs._isAdminContext).toBe(true);
|
|
187
225
|
});
|
|
188
226
|
});
|
|
@@ -208,7 +246,8 @@ describe('AdminScriptContext', () => {
|
|
|
208
246
|
});
|
|
209
247
|
|
|
210
248
|
it('calls QueuerUtil.send with correct params', async () => {
|
|
211
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
249
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
250
|
+
'https://sqs.us-east-1.amazonaws.com/123456789012/admin-scripts';
|
|
212
251
|
const ctx = new AdminFriggCommands({ executionId: 'exec_123' });
|
|
213
252
|
const params = { integrationId: 'int_456' };
|
|
214
253
|
|
|
@@ -226,7 +265,8 @@ describe('AdminScriptContext', () => {
|
|
|
226
265
|
});
|
|
227
266
|
|
|
228
267
|
it('includes parentExecutionId from constructor', async () => {
|
|
229
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
268
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
269
|
+
'https://sqs.example.com/queue';
|
|
230
270
|
const ctx = new AdminFriggCommands({ executionId: 'exec_parent' });
|
|
231
271
|
|
|
232
272
|
await ctx.queueScript('my-script', {});
|
|
@@ -236,7 +276,8 @@ describe('AdminScriptContext', () => {
|
|
|
236
276
|
});
|
|
237
277
|
|
|
238
278
|
it('logs queuing operation', async () => {
|
|
239
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
279
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
280
|
+
'https://sqs.example.com/queue';
|
|
240
281
|
const ctx = new AdminFriggCommands();
|
|
241
282
|
const params = { batchId: 'batch_1' };
|
|
242
283
|
|
|
@@ -271,7 +312,8 @@ describe('AdminScriptContext', () => {
|
|
|
271
312
|
});
|
|
272
313
|
|
|
273
314
|
it('calls QueuerUtil.batchSend', async () => {
|
|
274
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
315
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
316
|
+
'https://sqs.example.com/queue';
|
|
275
317
|
const ctx = new AdminFriggCommands({ executionId: 'exec_123' });
|
|
276
318
|
const entries = [
|
|
277
319
|
{ scriptName: 'script-1', params: { id: '1' } },
|
|
@@ -300,7 +342,8 @@ describe('AdminScriptContext', () => {
|
|
|
300
342
|
});
|
|
301
343
|
|
|
302
344
|
it('maps entries correctly', async () => {
|
|
303
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
345
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
346
|
+
'https://sqs.example.com/queue';
|
|
304
347
|
const ctx = new AdminFriggCommands();
|
|
305
348
|
const entries = [
|
|
306
349
|
{ scriptName: 'test-script', params: { value: 'abc' } },
|
|
@@ -316,11 +359,10 @@ describe('AdminScriptContext', () => {
|
|
|
316
359
|
});
|
|
317
360
|
|
|
318
361
|
it('handles entries without params', async () => {
|
|
319
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
362
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
363
|
+
'https://sqs.example.com/queue';
|
|
320
364
|
const ctx = new AdminFriggCommands();
|
|
321
|
-
const entries = [
|
|
322
|
-
{ scriptName: 'no-params-script' },
|
|
323
|
-
];
|
|
365
|
+
const entries = [{ scriptName: 'no-params-script' }];
|
|
324
366
|
|
|
325
367
|
await ctx.queueScriptBatch(entries);
|
|
326
368
|
|
|
@@ -329,7 +371,8 @@ describe('AdminScriptContext', () => {
|
|
|
329
371
|
});
|
|
330
372
|
|
|
331
373
|
it('logs batch queuing operation', async () => {
|
|
332
|
-
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
374
|
+
process.env.ADMIN_SCRIPT_QUEUE_URL =
|
|
375
|
+
'https://sqs.example.com/queue';
|
|
333
376
|
const ctx = new AdminFriggCommands();
|
|
334
377
|
const entries = [
|
|
335
378
|
{ scriptName: 'script-1', params: {} },
|
|
@@ -47,9 +47,13 @@ describe('AdminScriptBase', () => {
|
|
|
47
47
|
|
|
48
48
|
it('should have clean display object without redundant fields', () => {
|
|
49
49
|
expect(AdminScriptBase.Definition.display).toBeDefined();
|
|
50
|
-
expect(AdminScriptBase.Definition.display.category).toBe(
|
|
50
|
+
expect(AdminScriptBase.Definition.display.category).toBe(
|
|
51
|
+
'maintenance'
|
|
52
|
+
);
|
|
51
53
|
expect(AdminScriptBase.Definition.display.label).toBeUndefined();
|
|
52
|
-
expect(
|
|
54
|
+
expect(
|
|
55
|
+
AdminScriptBase.Definition.display.description
|
|
56
|
+
).toBeUndefined();
|
|
53
57
|
});
|
|
54
58
|
});
|
|
55
59
|
|
|
@@ -170,7 +174,10 @@ describe('AdminScriptBase', () => {
|
|
|
170
174
|
this.context.log('debug', 'Processing', params);
|
|
171
175
|
|
|
172
176
|
if (this.integrationFactory) {
|
|
173
|
-
this.context.log(
|
|
177
|
+
this.context.log(
|
|
178
|
+
'info',
|
|
179
|
+
'Integration factory available'
|
|
180
|
+
);
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
return { processed: true };
|
|
@@ -190,9 +197,19 @@ describe('AdminScriptBase', () => {
|
|
|
190
197
|
expect(result).toEqual({ processed: true });
|
|
191
198
|
|
|
192
199
|
expect(mockContext.log).toHaveBeenCalledTimes(3);
|
|
193
|
-
expect(mockContext.log).toHaveBeenCalledWith(
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
expect(mockContext.log).toHaveBeenCalledWith(
|
|
201
|
+
'info',
|
|
202
|
+
'Starting execution'
|
|
203
|
+
);
|
|
204
|
+
expect(mockContext.log).toHaveBeenCalledWith(
|
|
205
|
+
'debug',
|
|
206
|
+
'Processing',
|
|
207
|
+
{ test: 'data' }
|
|
208
|
+
);
|
|
209
|
+
expect(mockContext.log).toHaveBeenCalledWith(
|
|
210
|
+
'info',
|
|
211
|
+
'Integration factory available'
|
|
212
|
+
);
|
|
196
213
|
});
|
|
197
214
|
});
|
|
198
215
|
});
|