@unito/integration-sdk 2.3.4 → 2.3.6
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/dist/src/index.cjs +22 -17
- package/dist/src/resources/logger.js +12 -12
- package/dist/src/resources/provider.d.ts +1 -1
- package/dist/src/resources/provider.js +10 -5
- package/dist/test/resources/logger.test.js +57 -0
- package/dist/test/resources/provider.test.js +31 -0
- package/package.json +1 -1
- package/src/resources/logger.ts +14 -13
- package/src/resources/provider.ts +16 -5
- package/test/resources/logger.test.ts +69 -0
- package/test/resources/provider.test.ts +39 -0
package/dist/src/index.cjs
CHANGED
|
@@ -165,9 +165,10 @@ class Logger {
|
|
|
165
165
|
const coloredMessage = Logger.colorize(message, processedLogs, logLevel);
|
|
166
166
|
const metadata = {
|
|
167
167
|
date: new Date(processedLogs.date).toISOString(),
|
|
168
|
-
...processedMetadata,
|
|
168
|
+
...(processedMetadata.error && { error: processedMetadata.error }),
|
|
169
169
|
};
|
|
170
|
-
|
|
170
|
+
const metadataString = Object.keys(metadata).length > 1 ? ` ${JSON.stringify(metadata, null, 2)}` : ` ${JSON.stringify(metadata)}`;
|
|
171
|
+
console[logLevel](`${coloredMessage}${metadataString}`);
|
|
171
172
|
}
|
|
172
173
|
else {
|
|
173
174
|
console[logLevel](JSON.stringify(processedLogs));
|
|
@@ -219,9 +220,8 @@ class Logger {
|
|
|
219
220
|
*/
|
|
220
221
|
static colorize(message, metadata, logLevel) {
|
|
221
222
|
if (!process.stdout.isTTY) {
|
|
222
|
-
return message
|
|
223
|
+
return `${logLevel}: ${message}`;
|
|
223
224
|
}
|
|
224
|
-
const logOutput = `${logLevel}: ${message}`;
|
|
225
225
|
// Extract status code from logs
|
|
226
226
|
let statusCode;
|
|
227
227
|
if (metadata.http && typeof metadata.http === 'object' && !Array.isArray(metadata.http)) {
|
|
@@ -236,28 +236,28 @@ class Logger {
|
|
|
236
236
|
// Color based on status code first
|
|
237
237
|
if (statusCode) {
|
|
238
238
|
if (statusCode >= 400) {
|
|
239
|
-
return util.styleText('red',
|
|
239
|
+
return `${util.styleText('red', logLevel)}: ${util.styleText('red', message)}`;
|
|
240
240
|
}
|
|
241
241
|
else if (statusCode >= 300) {
|
|
242
|
-
return util.styleText('yellow',
|
|
242
|
+
return `${util.styleText('yellow', logLevel)}: ${util.styleText('yellow', message)}`;
|
|
243
243
|
}
|
|
244
244
|
else if (statusCode >= 200) {
|
|
245
|
-
return util.styleText('green',
|
|
245
|
+
return `${util.styleText('green', logLevel)}: ${util.styleText('green', message)}`;
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
// Fall back to log level if no status code found
|
|
249
249
|
switch (logLevel) {
|
|
250
250
|
case LogLevel.ERROR:
|
|
251
|
-
return util.styleText('red',
|
|
251
|
+
return `${util.styleText('red', logLevel)}: ${util.styleText('red', message)}`;
|
|
252
252
|
case LogLevel.WARN:
|
|
253
|
-
return util.styleText('yellow',
|
|
253
|
+
return `${util.styleText('yellow', logLevel)}: ${util.styleText('yellow', message)}`;
|
|
254
254
|
case LogLevel.INFO:
|
|
255
255
|
case LogLevel.LOG:
|
|
256
|
-
return util.styleText('green',
|
|
256
|
+
return `${util.styleText('green', logLevel)}: ${util.styleText('green', message)}`;
|
|
257
257
|
case LogLevel.DEBUG:
|
|
258
|
-
return util.styleText('cyan',
|
|
258
|
+
return `${util.styleText('cyan', logLevel)}: ${util.styleText('cyan', message)}`;
|
|
259
259
|
default:
|
|
260
|
-
return
|
|
260
|
+
return `${logLevel}: ${message}`;
|
|
261
261
|
}
|
|
262
262
|
}
|
|
263
263
|
}
|
|
@@ -1440,13 +1440,18 @@ class Provider {
|
|
|
1440
1440
|
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
1441
1441
|
* @returns The {@link Response} extracted from the provider.
|
|
1442
1442
|
*/
|
|
1443
|
-
async delete(endpoint, options) {
|
|
1444
|
-
|
|
1443
|
+
async delete(endpoint, options, body = null) {
|
|
1444
|
+
const defaultHeaders = {
|
|
1445
|
+
Accept: 'application/json',
|
|
1446
|
+
};
|
|
1447
|
+
// Only add Content-Type header when body is provided
|
|
1448
|
+
if (body !== null) {
|
|
1449
|
+
defaultHeaders['Content-Type'] = 'application/json';
|
|
1450
|
+
}
|
|
1451
|
+
return this.fetchWrapper(endpoint, body, {
|
|
1445
1452
|
...options,
|
|
1446
1453
|
method: 'DELETE',
|
|
1447
|
-
defaultHeaders
|
|
1448
|
-
Accept: 'application/json',
|
|
1449
|
-
},
|
|
1454
|
+
defaultHeaders,
|
|
1450
1455
|
});
|
|
1451
1456
|
}
|
|
1452
1457
|
generateAbsoluteUrl(providerUrl, endpoint, queryParams) {
|
|
@@ -137,9 +137,10 @@ export default class Logger {
|
|
|
137
137
|
const coloredMessage = Logger.colorize(message, processedLogs, logLevel);
|
|
138
138
|
const metadata = {
|
|
139
139
|
date: new Date(processedLogs.date).toISOString(),
|
|
140
|
-
...processedMetadata,
|
|
140
|
+
...(processedMetadata.error && { error: processedMetadata.error }),
|
|
141
141
|
};
|
|
142
|
-
|
|
142
|
+
const metadataString = Object.keys(metadata).length > 1 ? ` ${JSON.stringify(metadata, null, 2)}` : ` ${JSON.stringify(metadata)}`;
|
|
143
|
+
console[logLevel](`${coloredMessage}${metadataString}`);
|
|
143
144
|
}
|
|
144
145
|
else {
|
|
145
146
|
console[logLevel](JSON.stringify(processedLogs));
|
|
@@ -191,9 +192,8 @@ export default class Logger {
|
|
|
191
192
|
*/
|
|
192
193
|
static colorize(message, metadata, logLevel) {
|
|
193
194
|
if (!process.stdout.isTTY) {
|
|
194
|
-
return message
|
|
195
|
+
return `${logLevel}: ${message}`;
|
|
195
196
|
}
|
|
196
|
-
const logOutput = `${logLevel}: ${message}`;
|
|
197
197
|
// Extract status code from logs
|
|
198
198
|
let statusCode;
|
|
199
199
|
if (metadata.http && typeof metadata.http === 'object' && !Array.isArray(metadata.http)) {
|
|
@@ -208,28 +208,28 @@ export default class Logger {
|
|
|
208
208
|
// Color based on status code first
|
|
209
209
|
if (statusCode) {
|
|
210
210
|
if (statusCode >= 400) {
|
|
211
|
-
return styleText('red',
|
|
211
|
+
return `${styleText('red', logLevel)}: ${styleText('red', message)}`;
|
|
212
212
|
}
|
|
213
213
|
else if (statusCode >= 300) {
|
|
214
|
-
return styleText('yellow',
|
|
214
|
+
return `${styleText('yellow', logLevel)}: ${styleText('yellow', message)}`;
|
|
215
215
|
}
|
|
216
216
|
else if (statusCode >= 200) {
|
|
217
|
-
return styleText('green',
|
|
217
|
+
return `${styleText('green', logLevel)}: ${styleText('green', message)}`;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
// Fall back to log level if no status code found
|
|
221
221
|
switch (logLevel) {
|
|
222
222
|
case LogLevel.ERROR:
|
|
223
|
-
return styleText('red',
|
|
223
|
+
return `${styleText('red', logLevel)}: ${styleText('red', message)}`;
|
|
224
224
|
case LogLevel.WARN:
|
|
225
|
-
return styleText('yellow',
|
|
225
|
+
return `${styleText('yellow', logLevel)}: ${styleText('yellow', message)}`;
|
|
226
226
|
case LogLevel.INFO:
|
|
227
227
|
case LogLevel.LOG:
|
|
228
|
-
return styleText('green',
|
|
228
|
+
return `${styleText('green', logLevel)}: ${styleText('green', message)}`;
|
|
229
229
|
case LogLevel.DEBUG:
|
|
230
|
-
return styleText('cyan',
|
|
230
|
+
return `${styleText('cyan', logLevel)}: ${styleText('cyan', message)}`;
|
|
231
231
|
default:
|
|
232
|
-
return
|
|
232
|
+
return `${logLevel}: ${message}`;
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
}
|
|
@@ -207,7 +207,7 @@ export declare class Provider {
|
|
|
207
207
|
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
208
208
|
* @returns The {@link Response} extracted from the provider.
|
|
209
209
|
*/
|
|
210
|
-
delete<T = undefined>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
|
|
210
|
+
delete<T = undefined>(endpoint: string, options: RequestOptions, body?: RequestBody | null): Promise<Response<T>>;
|
|
211
211
|
private generateAbsoluteUrl;
|
|
212
212
|
private fetchWrapper;
|
|
213
213
|
private handleError;
|
|
@@ -241,13 +241,18 @@ export class Provider {
|
|
|
241
241
|
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
242
242
|
* @returns The {@link Response} extracted from the provider.
|
|
243
243
|
*/
|
|
244
|
-
async delete(endpoint, options) {
|
|
245
|
-
|
|
244
|
+
async delete(endpoint, options, body = null) {
|
|
245
|
+
const defaultHeaders = {
|
|
246
|
+
Accept: 'application/json',
|
|
247
|
+
};
|
|
248
|
+
// Only add Content-Type header when body is provided
|
|
249
|
+
if (body !== null) {
|
|
250
|
+
defaultHeaders['Content-Type'] = 'application/json';
|
|
251
|
+
}
|
|
252
|
+
return this.fetchWrapper(endpoint, body, {
|
|
246
253
|
...options,
|
|
247
254
|
method: 'DELETE',
|
|
248
|
-
defaultHeaders
|
|
249
|
-
Accept: 'application/json',
|
|
250
|
-
},
|
|
255
|
+
defaultHeaders,
|
|
251
256
|
});
|
|
252
257
|
}
|
|
253
258
|
generateAbsoluteUrl(providerUrl, endpoint, queryParams) {
|
|
@@ -250,4 +250,61 @@ describe('Logger', () => {
|
|
|
250
250
|
});
|
|
251
251
|
}
|
|
252
252
|
});
|
|
253
|
+
it('only logs date for metadata of successful requests in development', testContext => {
|
|
254
|
+
const originalEnv = process.env.NODE_ENV;
|
|
255
|
+
try {
|
|
256
|
+
process.env.NODE_ENV = 'development';
|
|
257
|
+
const infoSpy = testContext.mock.method(global.console, 'info', () => { });
|
|
258
|
+
const metadata = {
|
|
259
|
+
correlation_id: '123456789',
|
|
260
|
+
http: { method: 'GET', status_code: 200 },
|
|
261
|
+
user_id: 'user123',
|
|
262
|
+
request_id: 'req456',
|
|
263
|
+
};
|
|
264
|
+
const logger = new Logger(metadata);
|
|
265
|
+
logger.info('Test message without error');
|
|
266
|
+
assert.strictEqual(infoSpy.mock.calls.length, 1);
|
|
267
|
+
const loggedOutput = infoSpy.mock.calls[0]?.arguments[0];
|
|
268
|
+
assert.ok(loggedOutput.includes('Test message without error'));
|
|
269
|
+
// Should only contain date in metadata JSON
|
|
270
|
+
const metadataMatch = loggedOutput.match(/({.*})/);
|
|
271
|
+
assert.ok(metadataMatch);
|
|
272
|
+
const parsedMetadata = JSON.parse(metadataMatch[1]);
|
|
273
|
+
assert.ok(parsedMetadata.date);
|
|
274
|
+
assert.strictEqual(Object.keys(parsedMetadata).length, 1); // What matters: Only the date
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
process.env.NODE_ENV = originalEnv;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
it('logs date & error stack for metadata of failed requests in development', testContext => {
|
|
281
|
+
const originalEnv = process.env.NODE_ENV;
|
|
282
|
+
try {
|
|
283
|
+
process.env.NODE_ENV = 'development';
|
|
284
|
+
const metadata = {
|
|
285
|
+
correlation_id: '123456789',
|
|
286
|
+
http: { method: 'GET', status_code: 200 },
|
|
287
|
+
user_id: 'user123',
|
|
288
|
+
request_id: 'req456',
|
|
289
|
+
};
|
|
290
|
+
const logger = new Logger(metadata);
|
|
291
|
+
// Test with error metadata
|
|
292
|
+
const errorSpy = testContext.mock.method(global.console, 'error', () => { });
|
|
293
|
+
logger.error('Test message with error', { error: { code: 500, message: 'Internal Server Error' } });
|
|
294
|
+
assert.strictEqual(errorSpy.mock.calls.length, 1);
|
|
295
|
+
const errorLoggedOutput = errorSpy.mock.calls[0]?.arguments[0];
|
|
296
|
+
assert.ok(errorLoggedOutput.includes('Test message with error'));
|
|
297
|
+
// Should contain both date and error in metadata JSON
|
|
298
|
+
const errorMetadataMatch = errorLoggedOutput.match(/({.*})/s);
|
|
299
|
+
assert.ok(errorMetadataMatch);
|
|
300
|
+
const parsedErrorMetadata = JSON.parse(errorMetadataMatch[1]);
|
|
301
|
+
assert.ok(parsedErrorMetadata.date);
|
|
302
|
+
assert.ok(parsedErrorMetadata.error);
|
|
303
|
+
assert.deepEqual(parsedErrorMetadata.error, { code: 500, message: 'Internal Server Error' });
|
|
304
|
+
assert.strictEqual(Object.keys(parsedErrorMetadata).length, 2); // What matters: Date and error only
|
|
305
|
+
}
|
|
306
|
+
finally {
|
|
307
|
+
process.env.NODE_ENV = originalEnv;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
253
310
|
});
|
|
@@ -404,6 +404,37 @@ describe('Provider', () => {
|
|
|
404
404
|
]);
|
|
405
405
|
assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
|
|
406
406
|
});
|
|
407
|
+
it('deleteWithBody', async (context) => {
|
|
408
|
+
const response = new Response('{"success": true}', {
|
|
409
|
+
status: 200,
|
|
410
|
+
headers: { 'Content-Type': 'application/json' },
|
|
411
|
+
});
|
|
412
|
+
const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
413
|
+
const requestBody = { webhookIds: [1, 2, 3] };
|
|
414
|
+
const actualResponse = await provider.delete('/webhook', {
|
|
415
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
416
|
+
logger: logger,
|
|
417
|
+
signal: new AbortController().signal,
|
|
418
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
419
|
+
}, requestBody);
|
|
420
|
+
assert.equal(fetchMock.mock.calls.length, 1);
|
|
421
|
+
assert.deepEqual(fetchMock.mock.calls[0]?.arguments, [
|
|
422
|
+
'www.myApi.com/webhook',
|
|
423
|
+
{
|
|
424
|
+
method: 'DELETE',
|
|
425
|
+
body: JSON.stringify(requestBody),
|
|
426
|
+
signal: new AbortController().signal,
|
|
427
|
+
headers: {
|
|
428
|
+
'Content-Type': 'application/json',
|
|
429
|
+
Accept: 'application/json',
|
|
430
|
+
'X-Custom-Provider-Header': 'value',
|
|
431
|
+
'X-Provider-Credential-Header': 'apikey#1111',
|
|
432
|
+
'X-Additional-Header': 'value1',
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
]);
|
|
436
|
+
assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { success: true } });
|
|
437
|
+
});
|
|
407
438
|
it('uses rate limiter if provided', async (context) => {
|
|
408
439
|
const mockRateLimiter = context.mock.fn((_context, request) => Promise.resolve(request()));
|
|
409
440
|
const rateLimitedProvider = new Provider({
|
package/package.json
CHANGED
package/src/resources/logger.ts
CHANGED
|
@@ -168,10 +168,13 @@ export default class Logger {
|
|
|
168
168
|
|
|
169
169
|
const metadata = {
|
|
170
170
|
date: new Date(processedLogs.date).toISOString(),
|
|
171
|
-
...processedMetadata,
|
|
171
|
+
...(processedMetadata.error && { error: processedMetadata.error }),
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
const metadataString =
|
|
175
|
+
Object.keys(metadata).length > 1 ? ` ${JSON.stringify(metadata, null, 2)}` : ` ${JSON.stringify(metadata)}`;
|
|
176
|
+
|
|
177
|
+
console[logLevel](`${coloredMessage}${metadataString}`);
|
|
175
178
|
} else {
|
|
176
179
|
console[logLevel](JSON.stringify(processedLogs));
|
|
177
180
|
}
|
|
@@ -228,11 +231,9 @@ export default class Logger {
|
|
|
228
231
|
*/
|
|
229
232
|
private static colorize(message: string, metadata: Value, logLevel: LogLevel): string {
|
|
230
233
|
if (!process.stdout.isTTY) {
|
|
231
|
-
return message
|
|
234
|
+
return `${logLevel}: ${message}`;
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
const logOutput = `${logLevel}: ${message}`;
|
|
235
|
-
|
|
236
237
|
// Extract status code from logs
|
|
237
238
|
let statusCode: number | undefined;
|
|
238
239
|
if (metadata.http && typeof metadata.http === 'object' && !Array.isArray(metadata.http)) {
|
|
@@ -248,27 +249,27 @@ export default class Logger {
|
|
|
248
249
|
// Color based on status code first
|
|
249
250
|
if (statusCode) {
|
|
250
251
|
if (statusCode >= 400) {
|
|
251
|
-
return styleText('red',
|
|
252
|
+
return `${styleText('red', logLevel)}: ${styleText('red', message)}`;
|
|
252
253
|
} else if (statusCode >= 300) {
|
|
253
|
-
return styleText('yellow',
|
|
254
|
+
return `${styleText('yellow', logLevel)}: ${styleText('yellow', message)}`;
|
|
254
255
|
} else if (statusCode >= 200) {
|
|
255
|
-
return styleText('green',
|
|
256
|
+
return `${styleText('green', logLevel)}: ${styleText('green', message)}`;
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
|
|
259
260
|
// Fall back to log level if no status code found
|
|
260
261
|
switch (logLevel) {
|
|
261
262
|
case LogLevel.ERROR:
|
|
262
|
-
return styleText('red',
|
|
263
|
+
return `${styleText('red', logLevel)}: ${styleText('red', message)}`;
|
|
263
264
|
case LogLevel.WARN:
|
|
264
|
-
return styleText('yellow',
|
|
265
|
+
return `${styleText('yellow', logLevel)}: ${styleText('yellow', message)}`;
|
|
265
266
|
case LogLevel.INFO:
|
|
266
267
|
case LogLevel.LOG:
|
|
267
|
-
return styleText('green',
|
|
268
|
+
return `${styleText('green', logLevel)}: ${styleText('green', message)}`;
|
|
268
269
|
case LogLevel.DEBUG:
|
|
269
|
-
return styleText('cyan',
|
|
270
|
+
return `${styleText('cyan', logLevel)}: ${styleText('cyan', message)}`;
|
|
270
271
|
default:
|
|
271
|
-
return
|
|
272
|
+
return `${logLevel}: ${message}`;
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
}
|
|
@@ -336,13 +336,24 @@ export class Provider {
|
|
|
336
336
|
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
337
337
|
* @returns The {@link Response} extracted from the provider.
|
|
338
338
|
*/
|
|
339
|
-
public async delete<T = undefined>(
|
|
340
|
-
|
|
339
|
+
public async delete<T = undefined>(
|
|
340
|
+
endpoint: string,
|
|
341
|
+
options: RequestOptions,
|
|
342
|
+
body: RequestBody | null = null,
|
|
343
|
+
): Promise<Response<T>> {
|
|
344
|
+
const defaultHeaders: { Accept: string; 'Content-Type'?: string } = {
|
|
345
|
+
Accept: 'application/json',
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// Only add Content-Type header when body is provided
|
|
349
|
+
if (body !== null) {
|
|
350
|
+
defaultHeaders['Content-Type'] = 'application/json';
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return this.fetchWrapper<T>(endpoint, body, {
|
|
341
354
|
...options,
|
|
342
355
|
method: 'DELETE',
|
|
343
|
-
defaultHeaders
|
|
344
|
-
Accept: 'application/json',
|
|
345
|
-
},
|
|
356
|
+
defaultHeaders,
|
|
346
357
|
});
|
|
347
358
|
}
|
|
348
359
|
|
|
@@ -291,4 +291,73 @@ describe('Logger', () => {
|
|
|
291
291
|
});
|
|
292
292
|
}
|
|
293
293
|
});
|
|
294
|
+
|
|
295
|
+
it('only logs date for metadata of successful requests in development', testContext => {
|
|
296
|
+
const originalEnv = process.env.NODE_ENV;
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
process.env.NODE_ENV = 'development';
|
|
300
|
+
|
|
301
|
+
const infoSpy = testContext.mock.method(global.console, 'info', () => {});
|
|
302
|
+
const metadata = {
|
|
303
|
+
correlation_id: '123456789',
|
|
304
|
+
http: { method: 'GET', status_code: 200 },
|
|
305
|
+
user_id: 'user123',
|
|
306
|
+
request_id: 'req456',
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const logger = new Logger(metadata);
|
|
310
|
+
|
|
311
|
+
logger.info('Test message without error');
|
|
312
|
+
assert.strictEqual(infoSpy.mock.calls.length, 1);
|
|
313
|
+
|
|
314
|
+
const loggedOutput = infoSpy.mock.calls[0]?.arguments[0];
|
|
315
|
+
assert.ok(loggedOutput.includes('Test message without error'));
|
|
316
|
+
|
|
317
|
+
// Should only contain date in metadata JSON
|
|
318
|
+
const metadataMatch = loggedOutput.match(/({.*})/);
|
|
319
|
+
assert.ok(metadataMatch);
|
|
320
|
+
const parsedMetadata = JSON.parse(metadataMatch[1]);
|
|
321
|
+
assert.ok(parsedMetadata.date);
|
|
322
|
+
assert.strictEqual(Object.keys(parsedMetadata).length, 1); // What matters: Only the date
|
|
323
|
+
} finally {
|
|
324
|
+
process.env.NODE_ENV = originalEnv;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('logs date & error stack for metadata of failed requests in development', testContext => {
|
|
329
|
+
const originalEnv = process.env.NODE_ENV;
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
process.env.NODE_ENV = 'development';
|
|
333
|
+
|
|
334
|
+
const metadata = {
|
|
335
|
+
correlation_id: '123456789',
|
|
336
|
+
http: { method: 'GET', status_code: 200 },
|
|
337
|
+
user_id: 'user123',
|
|
338
|
+
request_id: 'req456',
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const logger = new Logger(metadata);
|
|
342
|
+
|
|
343
|
+
// Test with error metadata
|
|
344
|
+
const errorSpy = testContext.mock.method(global.console, 'error', () => {});
|
|
345
|
+
logger.error('Test message with error', { error: { code: 500, message: 'Internal Server Error' } });
|
|
346
|
+
assert.strictEqual(errorSpy.mock.calls.length, 1);
|
|
347
|
+
|
|
348
|
+
const errorLoggedOutput = errorSpy.mock.calls[0]?.arguments[0];
|
|
349
|
+
assert.ok(errorLoggedOutput.includes('Test message with error'));
|
|
350
|
+
|
|
351
|
+
// Should contain both date and error in metadata JSON
|
|
352
|
+
const errorMetadataMatch = errorLoggedOutput.match(/({.*})/s);
|
|
353
|
+
assert.ok(errorMetadataMatch);
|
|
354
|
+
const parsedErrorMetadata = JSON.parse(errorMetadataMatch[1]);
|
|
355
|
+
assert.ok(parsedErrorMetadata.date);
|
|
356
|
+
assert.ok(parsedErrorMetadata.error);
|
|
357
|
+
assert.deepEqual(parsedErrorMetadata.error, { code: 500, message: 'Internal Server Error' });
|
|
358
|
+
assert.strictEqual(Object.keys(parsedErrorMetadata).length, 2); // What matters: Date and error only
|
|
359
|
+
} finally {
|
|
360
|
+
process.env.NODE_ENV = originalEnv;
|
|
361
|
+
}
|
|
362
|
+
});
|
|
294
363
|
});
|
|
@@ -486,6 +486,45 @@ describe('Provider', () => {
|
|
|
486
486
|
assert.deepEqual(actualResponse, { status: 204, headers: response.headers, body: undefined });
|
|
487
487
|
});
|
|
488
488
|
|
|
489
|
+
it('deleteWithBody', async context => {
|
|
490
|
+
const response = new Response('{"success": true}', {
|
|
491
|
+
status: 200,
|
|
492
|
+
headers: { 'Content-Type': 'application/json' },
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
const fetchMock = context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
496
|
+
|
|
497
|
+
const requestBody = { webhookIds: [1, 2, 3] };
|
|
498
|
+
const actualResponse = await provider.delete(
|
|
499
|
+
'/webhook',
|
|
500
|
+
{
|
|
501
|
+
credentials: { apiKey: 'apikey#1111', unitoCredentialId: '123' },
|
|
502
|
+
logger: logger,
|
|
503
|
+
signal: new AbortController().signal,
|
|
504
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
505
|
+
},
|
|
506
|
+
requestBody,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
assert.equal(fetchMock.mock.calls.length, 1);
|
|
510
|
+
assert.deepEqual(fetchMock.mock.calls[0]?.arguments, [
|
|
511
|
+
'www.myApi.com/webhook',
|
|
512
|
+
{
|
|
513
|
+
method: 'DELETE',
|
|
514
|
+
body: JSON.stringify(requestBody),
|
|
515
|
+
signal: new AbortController().signal,
|
|
516
|
+
headers: {
|
|
517
|
+
'Content-Type': 'application/json',
|
|
518
|
+
Accept: 'application/json',
|
|
519
|
+
'X-Custom-Provider-Header': 'value',
|
|
520
|
+
'X-Provider-Credential-Header': 'apikey#1111',
|
|
521
|
+
'X-Additional-Header': 'value1',
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
]);
|
|
525
|
+
assert.deepEqual(actualResponse, { status: 200, headers: response.headers, body: { success: true } });
|
|
526
|
+
});
|
|
527
|
+
|
|
489
528
|
it('uses rate limiter if provided', async context => {
|
|
490
529
|
const mockRateLimiter = context.mock.fn((_context, request) => Promise.resolve(request()));
|
|
491
530
|
|