@friggframework/admin-scripts 2.0.0--canary.522.cbd3d5a.0 → 2.0.0--canary.517.35ee143.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/index.js +2 -2
- package/package.json +6 -9
- package/src/application/__tests__/admin-frigg-commands.test.js +19 -19
- package/src/application/__tests__/admin-script-base.test.js +2 -2
- package/src/application/__tests__/script-runner.test.js +146 -16
- package/src/application/admin-frigg-commands.js +8 -8
- package/src/application/admin-script-base.js +3 -5
- package/src/application/script-runner.js +125 -129
- package/src/application/use-cases/__tests__/delete-schedule-use-case.test.js +168 -0
- package/src/application/use-cases/__tests__/get-effective-schedule-use-case.test.js +114 -0
- package/src/application/use-cases/__tests__/upsert-schedule-use-case.test.js +201 -0
- package/src/application/use-cases/delete-schedule-use-case.js +108 -0
- package/src/application/use-cases/get-effective-schedule-use-case.js +78 -0
- package/src/application/use-cases/index.js +18 -0
- package/src/application/use-cases/upsert-schedule-use-case.js +127 -0
- package/src/builtins/__tests__/integration-health-check.test.js +1 -1
- package/src/builtins/__tests__/oauth-token-refresh.test.js +1 -1
- package/src/builtins/integration-health-check.js +1 -1
- package/src/builtins/oauth-token-refresh.js +1 -1
- package/src/infrastructure/__tests__/admin-auth-middleware.test.js +32 -95
- package/src/infrastructure/__tests__/admin-script-router.test.js +46 -47
- package/src/infrastructure/admin-auth-middleware.js +5 -43
- package/src/infrastructure/admin-script-router.js +38 -32
- package/src/infrastructure/script-executor-handler.js +2 -2
- 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
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const serverless = require('serverless-http');
|
|
3
|
-
const {
|
|
3
|
+
const { validateAdminApiKey } = require('./admin-auth-middleware');
|
|
4
4
|
const { getScriptFactory } = require('../application/script-factory');
|
|
5
5
|
const { createScriptRunner } = require('../application/script-runner');
|
|
6
6
|
const { createAdminScriptCommands } = require('@friggframework/core/application/commands/admin-script-commands');
|
|
7
7
|
const { QueuerUtil } = require('@friggframework/core/queues');
|
|
8
8
|
const { createSchedulerAdapter } = require('../adapters/scheduler-adapter-factory');
|
|
9
|
-
const {
|
|
9
|
+
const {
|
|
10
|
+
GetEffectiveScheduleUseCase,
|
|
11
|
+
UpsertScheduleUseCase,
|
|
12
|
+
DeleteScheduleUseCase,
|
|
13
|
+
} = require('../application/use-cases');
|
|
10
14
|
|
|
11
15
|
const router = express.Router();
|
|
12
16
|
|
|
13
17
|
// Apply auth middleware to all admin routes
|
|
14
|
-
router.use(
|
|
18
|
+
router.use(validateAdminApiKey);
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
|
-
* Create
|
|
21
|
+
* Create schedule use case instances
|
|
18
22
|
* @private
|
|
19
23
|
*/
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
function createScheduleUseCases() {
|
|
25
|
+
const commands = createAdminScriptCommands();
|
|
26
|
+
const schedulerAdapter = createSchedulerAdapter();
|
|
27
|
+
const scriptFactory = getScriptFactory();
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
getEffectiveSchedule: new GetEffectiveScheduleUseCase({ commands, scriptFactory }),
|
|
31
|
+
upsertSchedule: new UpsertScheduleUseCase({ commands, schedulerAdapter, scriptFactory }),
|
|
32
|
+
deleteSchedule: new DeleteScheduleUseCase({ commands, schedulerAdapter, scriptFactory }),
|
|
33
|
+
};
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
/**
|
|
@@ -40,8 +48,8 @@ router.get('/scripts', async (req, res) => {
|
|
|
40
48
|
version: s.definition.version,
|
|
41
49
|
description: s.definition.description,
|
|
42
50
|
category: s.definition.display?.category || 'custom',
|
|
43
|
-
|
|
44
|
-
s.definition.config?.
|
|
51
|
+
requireIntegrationInstance:
|
|
52
|
+
s.definition.config?.requireIntegrationInstance || false,
|
|
45
53
|
schedule: s.definition.schedule || null,
|
|
46
54
|
})),
|
|
47
55
|
});
|
|
@@ -87,10 +95,10 @@ router.get('/scripts/:scriptName', async (req, res) => {
|
|
|
87
95
|
});
|
|
88
96
|
|
|
89
97
|
/**
|
|
90
|
-
* POST /admin/scripts/:scriptName
|
|
98
|
+
* POST /admin/scripts/:scriptName
|
|
91
99
|
* Execute a script (sync, async, or dry-run)
|
|
92
100
|
*/
|
|
93
|
-
router.post('/scripts/:scriptName
|
|
101
|
+
router.post('/scripts/:scriptName', async (req, res) => {
|
|
94
102
|
try {
|
|
95
103
|
const { scriptName } = req.params;
|
|
96
104
|
const { params = {}, mode = 'async', dryRun = false } = req.body;
|
|
@@ -110,7 +118,6 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
110
118
|
trigger: 'MANUAL',
|
|
111
119
|
mode: 'sync',
|
|
112
120
|
dryRun: true,
|
|
113
|
-
audit: req.adminAudit,
|
|
114
121
|
});
|
|
115
122
|
return res.json(result);
|
|
116
123
|
}
|
|
@@ -121,20 +128,18 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
121
128
|
const result = await runner.execute(scriptName, params, {
|
|
122
129
|
trigger: 'MANUAL',
|
|
123
130
|
mode: 'sync',
|
|
124
|
-
audit: req.adminAudit,
|
|
125
131
|
});
|
|
126
132
|
return res.json(result);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
// Async execution - queue and return immediately
|
|
130
136
|
const commands = createAdminScriptCommands();
|
|
131
|
-
const execution = await commands.
|
|
137
|
+
const execution = await commands.createAdminProcess({
|
|
132
138
|
scriptName,
|
|
133
139
|
scriptVersion: factory.get(scriptName).Definition.version,
|
|
134
140
|
trigger: 'MANUAL',
|
|
135
141
|
mode: 'async',
|
|
136
142
|
input: params,
|
|
137
|
-
audit: req.adminAudit,
|
|
138
143
|
});
|
|
139
144
|
|
|
140
145
|
// Queue the execution
|
|
@@ -161,14 +166,14 @@ router.post('/scripts/:scriptName/execute', async (req, res) => {
|
|
|
161
166
|
});
|
|
162
167
|
|
|
163
168
|
/**
|
|
164
|
-
* GET /admin/executions/:executionId
|
|
165
|
-
* Get execution status
|
|
169
|
+
* GET /admin/scripts/:scriptName/executions/:executionId
|
|
170
|
+
* Get execution status for specific script
|
|
166
171
|
*/
|
|
167
|
-
router.get('/executions/:executionId', async (req, res) => {
|
|
172
|
+
router.get('/scripts/:scriptName/executions/:executionId', async (req, res) => {
|
|
168
173
|
try {
|
|
169
174
|
const { executionId } = req.params;
|
|
170
175
|
const commands = createAdminScriptCommands();
|
|
171
|
-
const execution = await commands.
|
|
176
|
+
const execution = await commands.findAdminProcessById(executionId);
|
|
172
177
|
|
|
173
178
|
if (execution.error) {
|
|
174
179
|
return res.status(execution.error).json({
|
|
@@ -185,12 +190,13 @@ router.get('/executions/:executionId', async (req, res) => {
|
|
|
185
190
|
});
|
|
186
191
|
|
|
187
192
|
/**
|
|
188
|
-
* GET /admin/executions
|
|
189
|
-
* List recent executions
|
|
193
|
+
* GET /admin/scripts/:scriptName/executions
|
|
194
|
+
* List recent executions for specific script
|
|
190
195
|
*/
|
|
191
|
-
router.get('/executions', async (req, res) => {
|
|
196
|
+
router.get('/scripts/:scriptName/executions', async (req, res) => {
|
|
192
197
|
try {
|
|
193
|
-
const { scriptName
|
|
198
|
+
const { scriptName } = req.params;
|
|
199
|
+
const { status, limit = 50 } = req.query;
|
|
194
200
|
const commands = createAdminScriptCommands();
|
|
195
201
|
|
|
196
202
|
const executions = await commands.findRecentExecutions({
|
|
@@ -213,9 +219,9 @@ router.get('/executions', async (req, res) => {
|
|
|
213
219
|
router.get('/scripts/:scriptName/schedule', async (req, res) => {
|
|
214
220
|
try {
|
|
215
221
|
const { scriptName } = req.params;
|
|
216
|
-
const
|
|
222
|
+
const { getEffectiveSchedule } = createScheduleUseCases();
|
|
217
223
|
|
|
218
|
-
const result = await
|
|
224
|
+
const result = await getEffectiveSchedule.execute(scriptName);
|
|
219
225
|
|
|
220
226
|
res.json({
|
|
221
227
|
source: result.source,
|
|
@@ -242,9 +248,9 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
242
248
|
try {
|
|
243
249
|
const { scriptName } = req.params;
|
|
244
250
|
const { enabled, cronExpression, timezone } = req.body;
|
|
245
|
-
const
|
|
251
|
+
const { upsertSchedule } = createScheduleUseCases();
|
|
246
252
|
|
|
247
|
-
const result = await
|
|
253
|
+
const result = await upsertSchedule.execute(scriptName, {
|
|
248
254
|
enabled,
|
|
249
255
|
cronExpression,
|
|
250
256
|
timezone,
|
|
@@ -283,9 +289,9 @@ router.put('/scripts/:scriptName/schedule', async (req, res) => {
|
|
|
283
289
|
router.delete('/scripts/:scriptName/schedule', async (req, res) => {
|
|
284
290
|
try {
|
|
285
291
|
const { scriptName } = req.params;
|
|
286
|
-
const
|
|
292
|
+
const { deleteSchedule } = createScheduleUseCases();
|
|
287
293
|
|
|
288
|
-
const result = await
|
|
294
|
+
const result = await deleteSchedule.execute(scriptName);
|
|
289
295
|
|
|
290
296
|
res.json(result);
|
|
291
297
|
} catch (error) {
|
|
@@ -21,7 +21,7 @@ async function handler(event) {
|
|
|
21
21
|
|
|
22
22
|
// If executionId provided (async from API), update existing record
|
|
23
23
|
if (executionId) {
|
|
24
|
-
await commands.
|
|
24
|
+
await commands.updateAdminProcessState(executionId, 'RUNNING');
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const result = await runner.execute(scriptName, params, {
|
|
@@ -45,7 +45,7 @@ async function handler(event) {
|
|
|
45
45
|
if (executionId) {
|
|
46
46
|
const commands = createAdminScriptCommands();
|
|
47
47
|
await commands
|
|
48
|
-
.
|
|
48
|
+
.completeAdminProcess(executionId, {
|
|
49
49
|
status: 'FAILED',
|
|
50
50
|
error: {
|
|
51
51
|
name: error.name,
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
createDryRunHttpClient,
|
|
3
|
-
injectDryRunHttpClient,
|
|
4
|
-
sanitizeHeaders,
|
|
5
|
-
sanitizeData,
|
|
6
|
-
detectService,
|
|
7
|
-
} = require('../dry-run-http-interceptor');
|
|
8
|
-
|
|
9
|
-
describe('Dry-Run HTTP Interceptor', () => {
|
|
10
|
-
describe('sanitizeHeaders', () => {
|
|
11
|
-
test('should redact authorization headers', () => {
|
|
12
|
-
const headers = {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
Authorization: 'Bearer secret-token',
|
|
15
|
-
'X-API-Key': 'api-key-123',
|
|
16
|
-
'User-Agent': 'frigg/1.0',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const sanitized = sanitizeHeaders(headers);
|
|
20
|
-
|
|
21
|
-
expect(sanitized['Content-Type']).toBe('application/json');
|
|
22
|
-
expect(sanitized['User-Agent']).toBe('frigg/1.0');
|
|
23
|
-
expect(sanitized.Authorization).toBe('[REDACTED]');
|
|
24
|
-
expect(sanitized['X-API-Key']).toBe('[REDACTED]');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('should handle case variations', () => {
|
|
28
|
-
const headers = {
|
|
29
|
-
authorization: 'Bearer token',
|
|
30
|
-
Authorization: 'Bearer token',
|
|
31
|
-
'x-api-key': 'key1',
|
|
32
|
-
'X-API-Key': 'key2',
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const sanitized = sanitizeHeaders(headers);
|
|
36
|
-
|
|
37
|
-
expect(sanitized.authorization).toBe('[REDACTED]');
|
|
38
|
-
expect(sanitized.Authorization).toBe('[REDACTED]');
|
|
39
|
-
expect(sanitized['x-api-key']).toBe('[REDACTED]');
|
|
40
|
-
expect(sanitized['X-API-Key']).toBe('[REDACTED]');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test('should handle null/undefined', () => {
|
|
44
|
-
expect(sanitizeHeaders(null)).toEqual({});
|
|
45
|
-
expect(sanitizeHeaders(undefined)).toEqual({});
|
|
46
|
-
expect(sanitizeHeaders({})).toEqual({});
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('detectService', () => {
|
|
51
|
-
test('should detect CRM services', () => {
|
|
52
|
-
expect(detectService('https://api.hubapi.com')).toBe('HubSpot');
|
|
53
|
-
expect(detectService('https://login.salesforce.com')).toBe('Salesforce');
|
|
54
|
-
expect(detectService('https://api.pipedrive.com')).toBe('Pipedrive');
|
|
55
|
-
expect(detectService('https://api.attio.com')).toBe('Attio');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('should detect communication services', () => {
|
|
59
|
-
expect(detectService('https://slack.com/api')).toBe('Slack');
|
|
60
|
-
expect(detectService('https://discord.com/api')).toBe('Discord');
|
|
61
|
-
expect(detectService('https://graph.teams.microsoft.com')).toBe('Microsoft Teams');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('should detect project management tools', () => {
|
|
65
|
-
expect(detectService('https://app.asana.com/api')).toBe('Asana');
|
|
66
|
-
expect(detectService('https://api.monday.com')).toBe('Monday.com');
|
|
67
|
-
expect(detectService('https://api.trello.com')).toBe('Trello');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test('should return unknown for unrecognized services', () => {
|
|
71
|
-
expect(detectService('https://example.com/api')).toBe('unknown');
|
|
72
|
-
expect(detectService(null)).toBe('unknown');
|
|
73
|
-
expect(detectService(undefined)).toBe('unknown');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('should be case insensitive', () => {
|
|
77
|
-
expect(detectService('HTTPS://API.HUBSPOT.COM')).toBe('HubSpot');
|
|
78
|
-
expect(detectService('https://API.SLACK.COM')).toBe('Slack');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('sanitizeData', () => {
|
|
83
|
-
test('should redact sensitive fields', () => {
|
|
84
|
-
const data = {
|
|
85
|
-
name: 'Test User',
|
|
86
|
-
email: 'test@example.com',
|
|
87
|
-
password: 'secret123',
|
|
88
|
-
apiToken: 'token-abc',
|
|
89
|
-
authKey: 'key-xyz',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const sanitized = sanitizeData(data);
|
|
93
|
-
|
|
94
|
-
expect(sanitized.name).toBe('Test User');
|
|
95
|
-
expect(sanitized.email).toBe('test@example.com');
|
|
96
|
-
expect(sanitized.password).toBe('[REDACTED]');
|
|
97
|
-
expect(sanitized.apiToken).toBe('[REDACTED]');
|
|
98
|
-
expect(sanitized.authKey).toBe('[REDACTED]');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test('should handle nested objects', () => {
|
|
102
|
-
const data = {
|
|
103
|
-
user: {
|
|
104
|
-
name: 'Test',
|
|
105
|
-
credentials: {
|
|
106
|
-
password: 'secret',
|
|
107
|
-
token: 'abc123',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const sanitized = sanitizeData(data);
|
|
113
|
-
|
|
114
|
-
expect(sanitized.user.name).toBe('Test');
|
|
115
|
-
expect(sanitized.user.credentials.password).toBe('[REDACTED]');
|
|
116
|
-
expect(sanitized.user.credentials.token).toBe('[REDACTED]');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test('should handle arrays', () => {
|
|
120
|
-
const data = [
|
|
121
|
-
{ id: '1', password: 'secret1' },
|
|
122
|
-
{ id: '2', apiKey: 'key2' },
|
|
123
|
-
];
|
|
124
|
-
|
|
125
|
-
const sanitized = sanitizeData(data);
|
|
126
|
-
|
|
127
|
-
expect(sanitized[0].id).toBe('1');
|
|
128
|
-
expect(sanitized[0].password).toBe('[REDACTED]');
|
|
129
|
-
expect(sanitized[1].apiKey).toBe('[REDACTED]');
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('should preserve primitives', () => {
|
|
133
|
-
expect(sanitizeData('string')).toBe('string');
|
|
134
|
-
expect(sanitizeData(123)).toBe(123);
|
|
135
|
-
expect(sanitizeData(true)).toBe(true);
|
|
136
|
-
expect(sanitizeData(null)).toBe(null);
|
|
137
|
-
expect(sanitizeData(undefined)).toBe(undefined);
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
describe('createDryRunHttpClient', () => {
|
|
142
|
-
let operationLog;
|
|
143
|
-
|
|
144
|
-
beforeEach(() => {
|
|
145
|
-
operationLog = [];
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test('should log GET requests', async () => {
|
|
149
|
-
const client = createDryRunHttpClient(operationLog);
|
|
150
|
-
|
|
151
|
-
const response = await client.get('/contacts', {
|
|
152
|
-
baseURL: 'https://api.hubapi.com',
|
|
153
|
-
headers: { Authorization: 'Bearer token' },
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
expect(operationLog).toHaveLength(1);
|
|
157
|
-
expect(operationLog[0]).toMatchObject({
|
|
158
|
-
operation: 'HTTP_REQUEST',
|
|
159
|
-
method: 'GET',
|
|
160
|
-
url: 'https://api.hubapi.com/contacts',
|
|
161
|
-
service: 'HubSpot',
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
expect(operationLog[0].headers.Authorization).toBe('[REDACTED]');
|
|
165
|
-
expect(response.data._dryRun).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('should log POST requests with data', async () => {
|
|
169
|
-
const client = createDryRunHttpClient(operationLog);
|
|
170
|
-
|
|
171
|
-
const postData = {
|
|
172
|
-
name: 'John Doe',
|
|
173
|
-
email: 'john@example.com',
|
|
174
|
-
password: 'secret123',
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
await client.post('/users', postData, {
|
|
178
|
-
baseURL: 'https://api.example.com',
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
expect(operationLog).toHaveLength(1);
|
|
182
|
-
expect(operationLog[0].method).toBe('POST');
|
|
183
|
-
expect(operationLog[0].data.name).toBe('John Doe');
|
|
184
|
-
expect(operationLog[0].data.email).toBe('john@example.com');
|
|
185
|
-
expect(operationLog[0].data.password).toBe('[REDACTED]');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test('should log PUT requests', async () => {
|
|
189
|
-
const client = createDryRunHttpClient(operationLog);
|
|
190
|
-
|
|
191
|
-
await client.put('/users/123', { status: 'active' }, {
|
|
192
|
-
baseURL: 'https://api.example.com',
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
expect(operationLog).toHaveLength(1);
|
|
196
|
-
expect(operationLog[0].method).toBe('PUT');
|
|
197
|
-
expect(operationLog[0].data.status).toBe('active');
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test('should log PATCH requests', async () => {
|
|
201
|
-
const client = createDryRunHttpClient(operationLog);
|
|
202
|
-
|
|
203
|
-
await client.patch('/users/123', { name: 'Updated' });
|
|
204
|
-
|
|
205
|
-
expect(operationLog).toHaveLength(1);
|
|
206
|
-
expect(operationLog[0].method).toBe('PATCH');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('should log DELETE requests', async () => {
|
|
210
|
-
const client = createDryRunHttpClient(operationLog);
|
|
211
|
-
|
|
212
|
-
await client.delete('/users/123', {
|
|
213
|
-
baseURL: 'https://api.example.com',
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
expect(operationLog).toHaveLength(1);
|
|
217
|
-
expect(operationLog[0].method).toBe('DELETE');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test('should return mock response', async () => {
|
|
221
|
-
const client = createDryRunHttpClient(operationLog);
|
|
222
|
-
|
|
223
|
-
const response = await client.get('/test');
|
|
224
|
-
|
|
225
|
-
expect(response.status).toBe(200);
|
|
226
|
-
expect(response.statusText).toContain('Dry-Run');
|
|
227
|
-
expect(response.data._dryRun).toBe(true);
|
|
228
|
-
expect(response.headers['x-dry-run']).toBe('true');
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test('should include query params in log', async () => {
|
|
232
|
-
const client = createDryRunHttpClient(operationLog);
|
|
233
|
-
|
|
234
|
-
await client.get('/search', {
|
|
235
|
-
baseURL: 'https://api.example.com',
|
|
236
|
-
params: { q: 'test', limit: 10 },
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
expect(operationLog[0].params).toEqual({ q: 'test', limit: 10 });
|
|
240
|
-
});
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
describe('injectDryRunHttpClient', () => {
|
|
244
|
-
let operationLog;
|
|
245
|
-
let dryRunClient;
|
|
246
|
-
|
|
247
|
-
beforeEach(() => {
|
|
248
|
-
operationLog = [];
|
|
249
|
-
dryRunClient = createDryRunHttpClient(operationLog);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test('should inject into primary API module', () => {
|
|
253
|
-
const integrationInstance = {
|
|
254
|
-
primary: {
|
|
255
|
-
api: {
|
|
256
|
-
_httpClient: { get: jest.fn() },
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
262
|
-
|
|
263
|
-
expect(integrationInstance.primary.api._httpClient).toBe(dryRunClient);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test('should inject into target API module', () => {
|
|
267
|
-
const integrationInstance = {
|
|
268
|
-
target: {
|
|
269
|
-
api: {
|
|
270
|
-
_httpClient: { get: jest.fn() },
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
276
|
-
|
|
277
|
-
expect(integrationInstance.target.api._httpClient).toBe(dryRunClient);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('should inject into both primary and target', () => {
|
|
281
|
-
const integrationInstance = {
|
|
282
|
-
primary: {
|
|
283
|
-
api: { _httpClient: { get: jest.fn() } },
|
|
284
|
-
},
|
|
285
|
-
target: {
|
|
286
|
-
api: { _httpClient: { get: jest.fn() } },
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
291
|
-
|
|
292
|
-
expect(integrationInstance.primary.api._httpClient).toBe(dryRunClient);
|
|
293
|
-
expect(integrationInstance.target.api._httpClient).toBe(dryRunClient);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test('should handle missing api modules gracefully', () => {
|
|
297
|
-
const integrationInstance = {
|
|
298
|
-
primary: {},
|
|
299
|
-
target: null,
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
expect(() => {
|
|
303
|
-
injectDryRunHttpClient(integrationInstance, dryRunClient);
|
|
304
|
-
}).not.toThrow();
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test('should handle null integration instance', () => {
|
|
308
|
-
expect(() => {
|
|
309
|
-
injectDryRunHttpClient(null, dryRunClient);
|
|
310
|
-
}).not.toThrow();
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
});
|