@friggframework/admin-scripts 2.0.0--canary.545.b4ca16d.0 → 2.0.0--canary.517.ff03f2c.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.
Files changed (45) hide show
  1. package/README.md +272 -0
  2. package/index.js +22 -19
  3. package/package.json +6 -6
  4. package/src/adapters/__tests__/aws-scheduler-adapter.test.js +106 -45
  5. package/src/adapters/__tests__/local-scheduler-adapter.test.js +24 -10
  6. package/src/adapters/__tests__/scheduler-adapter-factory.test.js +30 -9
  7. package/src/adapters/__tests__/scheduler-adapter.test.js +6 -2
  8. package/src/adapters/aws-scheduler-adapter.js +55 -28
  9. package/src/adapters/local-scheduler-adapter.js +2 -2
  10. package/src/adapters/scheduler-adapter-factory.js +3 -1
  11. package/src/adapters/scheduler-adapter.js +9 -3
  12. package/src/application/__tests__/admin-frigg-commands.test.js +73 -30
  13. package/src/application/__tests__/admin-script-base.test.js +23 -6
  14. package/src/application/__tests__/script-factory.test.js +30 -6
  15. package/src/application/__tests__/script-runner.test.js +113 -24
  16. package/src/application/__tests__/validate-script-input.test.js +54 -15
  17. package/src/application/admin-frigg-commands.js +21 -9
  18. package/src/application/admin-script-base.js +3 -2
  19. package/src/application/script-factory.js +3 -1
  20. package/src/application/script-runner.js +90 -48
  21. package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +16 -7
  22. package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +9 -4
  23. package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +21 -10
  24. package/src/application/use-cases/delete-schedule-use-case.js +6 -4
  25. package/src/application/use-cases/get-effective-schedule-use-case.js +3 -1
  26. package/src/application/use-cases/index.js +3 -1
  27. package/src/application/use-cases/upsert-schedule-use-case.js +30 -11
  28. package/src/application/validate-script-input.js +10 -3
  29. package/src/infrastructure/__tests__/admin-auth-middleware.test.js +9 -3
  30. package/src/infrastructure/__tests__/admin-script-router.test.js +125 -52
  31. package/src/infrastructure/admin-auth-middleware.js +3 -1
  32. package/src/infrastructure/admin-script-router.js +129 -14
  33. package/src/infrastructure/bootstrap.js +82 -0
  34. package/src/infrastructure/script-executor-handler.js +119 -52
  35. package/src/application/__tests__/dry-run-http-interceptor.test.js +0 -313
  36. package/src/application/__tests__/dry-run-repository-wrapper.test.js +0 -257
  37. package/src/application/__tests__/schedule-management-use-case.test.js +0 -276
  38. package/src/application/dry-run-http-interceptor.js +0 -296
  39. package/src/application/dry-run-repository-wrapper.js +0 -261
  40. package/src/application/schedule-management-use-case.js +0 -230
  41. package/src/builtins/__tests__/integration-health-check.test.js +0 -607
  42. package/src/builtins/__tests__/oauth-token-refresh.test.js +0 -354
  43. package/src/builtins/index.js +0 -28
  44. package/src/builtins/integration-health-check.js +0 -278
  45. package/src/builtins/oauth-token-refresh.js +0 -220
@@ -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('America/New_York');
58
- expect(JSON.parse(schedule.Target.Input).params).toEqual({ key: 'value' });
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(adapter.deleteSchedule('non-existent')).resolves.toBeUndefined();
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('frigg-script-script-1');
214
- expect(schedules.map((s) => s.Name)).toContain('frigg-script-script-2');
215
- expect(schedules.map((s) => s.Name)).toContain('frigg-script-script-3');
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('America/New_York');
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({ type: 'aws', ...awsAdapterParams });
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({ type: 'eventbridge', ...awsAdapterParams });
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({ type: 'AWS', ...awsAdapterParams });
70
+ const adapter1 = createSchedulerAdapter({
71
+ type: 'AWS',
72
+ ...awsAdapterParams,
73
+ });
65
74
  const adapter2 = createSchedulerAdapter({ type: 'LOCAL' });
66
- const adapter3 = createSchedulerAdapter({ type: 'EventBridge', ...awsAdapterParams });
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: 'arn:aws:lambda:eu-west-1:123456789012:function:test',
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('arn:aws:lambda:eu-west-1:123456789012:function:test');
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('arn:aws:iam::123456789012:role/custom-role');
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('arn:aws:iam::999999999999:role/scheduler-role');
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(() => createSchedulerAdapter({ type: 'unknown-type' })).toThrow();
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(adapter.setScheduleEnabled('test', true)).rejects.toThrow(
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(incomplete.setScheduleEnabled('test', true)).rejects.toThrow();
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, CreateScheduleCommand, DeleteScheduleCommand,
5
- GetScheduleCommand, UpdateScheduleCommand, ListSchedulesCommand;
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({ credentials, targetLambdaArn, scheduleGroupName, roleArn } = {}) {
32
+ constructor({
33
+ credentials,
34
+ targetLambdaArn,
35
+ scheduleGroupName,
36
+ roleArn,
37
+ } = {}) {
29
38
  super();
30
- if (!targetLambdaArn) throw new Error('AWSSchedulerAdapter requires targetLambdaArn');
31
- if (!scheduleGroupName) throw new Error('AWSSchedulerAdapter requires scheduleGroupName');
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) throw new Error('AWSSchedulerAdapter requires AWS_REGION environment variable');
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(new CreateScheduleCommand(scheduleParams));
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(new UpdateScheduleCommand(scheduleParams));
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(new DeleteScheduleCommand({
104
- Name: scheduleName,
105
- GroupName: this.scheduleGroupName,
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(new UpdateScheduleCommand({
123
- Name: scheduleName,
124
- GroupName: this.scheduleGroupName,
125
- ScheduleExpression: currentSchedule.ScheduleExpression,
126
- ScheduleExpressionTimezone: currentSchedule.ScheduleExpressionTimezone,
127
- FlexibleTimeWindow: currentSchedule.FlexibleTimeWindow,
128
- Target: currentSchedule.Target,
129
- State: enabled ? 'ENABLED' : 'DISABLED',
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(new ListSchedulesCommand({
137
- GroupName: this.scheduleGroupName,
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(new GetScheduleCommand({
148
- Name: scheduleName,
149
- GroupName: this.scheduleGroupName,
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('Scheduler adapter type is required. Configure in appDefinition.adminScripts.scheduler.type');
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('SchedulerAdapter.createSchedule() must be implemented');
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('SchedulerAdapter.deleteSchedule() must be implemented');
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('SchedulerAdapter.setScheduleEnabled() must be implemented');
47
+ throw new Error(
48
+ 'SchedulerAdapter.setScheduleEnabled() must be implemented'
49
+ );
44
50
  }
45
51
 
46
52
  /**
@@ -1,10 +1,19 @@
1
- const { AdminFriggCommands, createAdminFriggCommands } = require('../admin-frigg-commands');
1
+ const {
2
+ AdminFriggCommands,
3
+ createAdminFriggCommands,
4
+ } = require('../admin-frigg-commands');
2
5
 
3
6
  // Mock all repository factories
4
- jest.mock('@friggframework/core/integrations/repositories/integration-repository-factory');
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('@friggframework/core/modules/repositories/module-repository-factory');
7
- jest.mock('@friggframework/core/credential/repositories/credential-repository-factory');
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 { createIntegrationRepository } = require('@friggframework/core/integrations/repositories/integration-repository-factory');
51
- const { createUserRepository } = require('@friggframework/core/user/repositories/user-repository-factory');
52
- const { createModuleRepository } = require('@friggframework/core/modules/repositories/module-repository-factory');
53
- const { createCredentialRepository } = require('@friggframework/core/credential/repositories/credential-repository-factory');
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({ integrationFactory: mockFactory });
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 { createIntegrationRepository } = require('@friggframework/core/integrations/repositories/integration-repository-factory');
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 { createUserRepository } = require('@friggframework/core/user/repositories/user-repository-factory');
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 { createModuleRepository } = require('@friggframework/core/modules/repositories/module-repository-factory');
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 { createCredentialRepository } = require('@friggframework/core/credential/repositories/credential-repository-factory');
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
- 'Set Definition.config.requireIntegrationInstance = true'
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.fn().mockResolvedValue(mockInstance),
190
+ getInstanceFromIntegrationId: jest
191
+ .fn()
192
+ .mockResolvedValue(mockInstance),
164
193
  };
165
- const ctx = new AdminFriggCommands({ integrationFactory: mockFactory });
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(mockFactory.getInstanceFromIntegrationId).toHaveBeenCalledWith({
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.fn().mockResolvedValue(mockInstance),
212
+ getInstanceFromIntegrationId: jest
213
+ .fn()
214
+ .mockResolvedValue(mockInstance),
180
215
  };
181
- const ctx = new AdminFriggCommands({ integrationFactory: mockFactory });
216
+ const ctx = new AdminFriggCommands({
217
+ integrationFactory: mockFactory,
218
+ });
182
219
 
183
220
  await ctx.instantiate('int_123');
184
221
 
185
- const callArgs = mockFactory.getInstanceFromIntegrationId.mock.calls[0][0];
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 = 'https://sqs.us-east-1.amazonaws.com/123456789012/admin-scripts';
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 = 'https://sqs.example.com/queue';
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 = 'https://sqs.example.com/queue';
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 = 'https://sqs.example.com/queue';
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 = 'https://sqs.example.com/queue';
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 = 'https://sqs.example.com/queue';
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 = 'https://sqs.example.com/queue';
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('maintenance');
50
+ expect(AdminScriptBase.Definition.display.category).toBe(
51
+ 'maintenance'
52
+ );
51
53
  expect(AdminScriptBase.Definition.display.label).toBeUndefined();
52
- expect(AdminScriptBase.Definition.display.description).toBeUndefined();
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('info', 'Integration factory available');
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('info', 'Starting execution');
194
- expect(mockContext.log).toHaveBeenCalledWith('debug', 'Processing', { test: 'data' });
195
- expect(mockContext.log).toHaveBeenCalledWith('info', 'Integration factory available');
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
  });