@api3/commons 0.4.0 → 0.5.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 (38) hide show
  1. package/README.md +2 -0
  2. package/dist/blockchain-utilities/derivation.d.ts +78 -0
  3. package/dist/blockchain-utilities/derivation.d.ts.map +1 -0
  4. package/dist/blockchain-utilities/derivation.js +97 -0
  5. package/dist/blockchain-utilities/derivation.js.map +1 -0
  6. package/dist/blockchain-utilities/schema.d.ts +12 -0
  7. package/dist/blockchain-utilities/schema.d.ts.map +1 -0
  8. package/dist/blockchain-utilities/schema.js +13 -0
  9. package/dist/blockchain-utilities/schema.js.map +1 -0
  10. package/dist/logger/index.d.ts +1 -1
  11. package/dist/logger/index.d.ts.map +1 -1
  12. package/dist/logger/index.js.map +1 -1
  13. package/dist/processing/processing.d.ts +70 -18
  14. package/dist/processing/processing.d.ts.map +1 -1
  15. package/dist/processing/processing.js +145 -37
  16. package/dist/processing/processing.js.map +1 -1
  17. package/dist/processing/schema.d.ts +25 -2
  18. package/dist/processing/schema.d.ts.map +1 -1
  19. package/dist/processing/schema.js +10 -9
  20. package/dist/processing/schema.js.map +1 -1
  21. package/dist/processing/unsafe-evaluate.d.ts +1 -0
  22. package/dist/processing/unsafe-evaluate.d.ts.map +1 -1
  23. package/dist/processing/unsafe-evaluate.js +31 -1
  24. package/dist/processing/unsafe-evaluate.js.map +1 -1
  25. package/package.json +5 -3
  26. package/src/blockchain-utilities/README.md +5 -0
  27. package/src/blockchain-utilities/derivation.test.ts +147 -0
  28. package/src/blockchain-utilities/derivation.ts +116 -0
  29. package/src/blockchain-utilities/schema.test.ts +23 -0
  30. package/src/blockchain-utilities/schema.ts +14 -0
  31. package/src/logger/index.test.ts +16 -0
  32. package/src/logger/index.ts +8 -5
  33. package/src/processing/README.md +89 -14
  34. package/src/processing/processing.test.ts +429 -111
  35. package/src/processing/processing.ts +196 -47
  36. package/src/processing/schema.ts +21 -8
  37. package/src/processing/unsafe-evaluate.test.ts +220 -1
  38. package/src/processing/unsafe-evaluate.ts +39 -0
@@ -1,32 +1,37 @@
1
1
  /* eslint-disable jest/prefer-strict-equal */ // Because the errors are thrown from the "vm" module (different context), they are not strictly equal.
2
+ import { ZodError } from 'zod';
3
+
2
4
  import { createEndpoint } from '../../test/fixtures';
3
5
 
4
6
  import {
5
7
  addReservedParameters,
6
- postProcessApiCallResponse,
7
- preProcessApiCallParameters,
8
+ postProcessResponse,
9
+ postProcessResponseV1,
10
+ postProcessResponseV2,
11
+ preProcessEndpointParameters,
12
+ preProcessEndpointParametersV1,
13
+ preProcessEndpointParametersV2,
8
14
  removeReservedParameters,
9
15
  } from './processing';
16
+ import type { ProcessingSpecificationV2, ProcessingSpecifications } from './schema';
10
17
 
11
- describe(preProcessApiCallParameters.name, () => {
18
+ describe(preProcessEndpointParametersV1.name, () => {
12
19
  it('valid processing code', async () => {
13
- const endpoint = createEndpoint({
14
- preProcessingSpecifications: [
15
- {
16
- environment: 'Node',
17
- value: 'const output = {...input, from: "ETH"};',
18
- timeoutMs: 5000,
19
- },
20
- {
21
- environment: 'Node',
22
- value: 'const output = {...input, newProp: "airnode"};',
23
- timeoutMs: 5000,
24
- },
25
- ],
26
- });
20
+ const preProcessingSpecifications = [
21
+ {
22
+ environment: 'Node',
23
+ value: 'const output = {...input, from: "ETH"};',
24
+ timeoutMs: 5000,
25
+ },
26
+ {
27
+ environment: 'Node',
28
+ value: 'const output = {...input, newProp: "airnode"};',
29
+ timeoutMs: 5000,
30
+ },
31
+ ] as ProcessingSpecifications;
27
32
  const parameters = { _type: 'int256', _path: 'price' };
28
33
 
29
- const result = await preProcessApiCallParameters(endpoint, parameters);
34
+ const result = await preProcessEndpointParametersV1(preProcessingSpecifications, parameters);
30
35
 
31
36
  expect(result).toEqual({
32
37
  _path: 'price',
@@ -37,43 +42,39 @@ describe(preProcessApiCallParameters.name, () => {
37
42
  });
38
43
 
39
44
  it('invalid processing code', async () => {
40
- const endpoint = createEndpoint({
41
- preProcessingSpecifications: [
42
- {
43
- environment: 'Node',
44
- value: 'something invalid; const output = {...input, from: `ETH`};',
45
- timeoutMs: 5000,
46
- },
47
- {
48
- environment: 'Node',
49
- value: 'const output = {...input, newProp: "airnode"};',
50
- timeoutMs: 5000,
51
- },
52
- ],
53
- });
45
+ const preProcessingSpecifications = [
46
+ {
47
+ environment: 'Node',
48
+ value: 'something invalid; const output = {...input, from: `ETH`};',
49
+ timeoutMs: 5000,
50
+ },
51
+ {
52
+ environment: 'Node',
53
+ value: 'const output = {...input, newProp: "airnode"};',
54
+ timeoutMs: 5000,
55
+ },
56
+ ] as ProcessingSpecifications;
54
57
  const parameters = { _type: 'int256', _path: 'price', from: 'TBD' };
55
58
 
56
- const throwingFunc = async () => preProcessApiCallParameters(endpoint, parameters);
59
+ const throwingFunc = async () => preProcessEndpointParametersV1(preProcessingSpecifications, parameters);
57
60
 
58
61
  await expect(throwingFunc).rejects.toEqual(new Error('SyntaxError: Unexpected identifier'));
59
62
  });
60
63
 
61
64
  it('demonstrates access to endpointParameters, but reserved parameters are inaccessible', async () => {
62
65
  const parameters = { _type: 'int256', _path: 'price', to: 'USD' };
63
- const endpoint = createEndpoint({
64
- preProcessingSpecifications: [
65
- {
66
- environment: 'Node',
67
- // pretend the user is trying to 1) override _path and 2) set a new parameter based on
68
- // the presence of the reserved parameter _type (which is inaccessible)
69
- value:
70
- 'const output = {...input, from: "ETH", _path: "price.newpath", myVal: input._type ? "123" : "456", newTo: endpointParameters.to };',
71
- timeoutMs: 5000,
72
- },
73
- ],
74
- });
75
-
76
- const result = await preProcessApiCallParameters(endpoint, parameters);
66
+ const preProcessingSpecifications = [
67
+ {
68
+ environment: 'Node',
69
+ // pretend the user is trying to 1) override _path and 2) set a new parameter based on
70
+ // the presence of the reserved parameter _type (which is inaccessible)
71
+ value:
72
+ 'const output = {...input, from: "ETH", _path: "price.newpath", myVal: input._type ? "123" : "456", newTo: endpointParameters.to };',
73
+ timeoutMs: 5000,
74
+ },
75
+ ] as ProcessingSpecifications;
76
+
77
+ const result = await preProcessEndpointParametersV1(preProcessingSpecifications, parameters);
77
78
 
78
79
  expect(result).toEqual({
79
80
  _path: 'price', // is not overridden
@@ -86,21 +87,19 @@ describe(preProcessApiCallParameters.name, () => {
86
87
  });
87
88
 
88
89
  it('uses native modules for processing', async () => {
89
- const endpoint = createEndpoint({
90
- preProcessingSpecifications: [
91
- {
92
- environment: 'Node',
93
- value: `
90
+ const preProcessingSpecifications = [
91
+ {
92
+ environment: 'Node',
93
+ value: `
94
94
  const randomValue = crypto.randomBytes(4).toString('hex');
95
95
  const output = {...input, randomValue};
96
96
  `,
97
- timeoutMs: 5000,
98
- },
99
- ],
100
- });
97
+ timeoutMs: 5000,
98
+ },
99
+ ] as ProcessingSpecifications;
101
100
  const parameters = { _type: 'int256', _path: 'price' };
102
101
 
103
- const result = await preProcessApiCallParameters(endpoint, parameters);
102
+ const result = await preProcessEndpointParametersV1(preProcessingSpecifications, parameters);
104
103
 
105
104
  // Check that the result contains the original parameters and a valid 8-character hex random value.
106
105
  expect(result).toMatchObject({
@@ -112,46 +111,42 @@ describe(preProcessApiCallParameters.name, () => {
112
111
  });
113
112
 
114
113
  it('throws error due to processing timeout', async () => {
115
- const endpoint = createEndpoint({
116
- preProcessingSpecifications: [
117
- {
118
- environment: 'Node async',
119
- value: `
114
+ const preProcessingSpecifications = [
115
+ {
116
+ environment: 'Node async',
117
+ value: `
120
118
  const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
121
119
  delay(5000);
122
120
  const output = {...input, from: 'ETH'};
123
121
  `,
124
- timeoutMs: 100, // This timeout is shorter than the delay in the processing code.
125
- },
126
- ],
127
- });
122
+ timeoutMs: 100, // This timeout is shorter than the delay in the processing code.
123
+ },
124
+ ] as ProcessingSpecifications;
128
125
  const parameters = { _type: 'int256', _path: 'price' };
129
126
 
130
- const throwingFunc = async () => preProcessApiCallParameters(endpoint, parameters);
127
+ const throwingFunc = async () => preProcessEndpointParametersV1(preProcessingSpecifications, parameters);
131
128
 
132
129
  await expect(throwingFunc).rejects.toThrow('Timeout exceeded');
133
130
  });
134
131
  });
135
132
 
136
- describe(postProcessApiCallResponse.name, () => {
133
+ describe(postProcessResponseV1.name, () => {
137
134
  it('processes valid code', async () => {
138
135
  const parameters = { _type: 'int256', _path: 'price' };
139
- const endpoint = createEndpoint({
140
- postProcessingSpecifications: [
141
- {
142
- environment: 'Node',
143
- value: 'const output = parseInt(input.price)*2;',
144
- timeoutMs: 5000,
145
- },
146
- {
147
- environment: 'Node',
148
- value: 'const output = parseInt(input)*2;',
149
- timeoutMs: 5000,
150
- },
151
- ],
152
- });
153
-
154
- const result = await postProcessApiCallResponse({ price: 1000 }, endpoint, parameters);
136
+ const postProcessingSpecifications = [
137
+ {
138
+ environment: 'Node',
139
+ value: 'const output = parseInt(input.price)*2;',
140
+ timeoutMs: 5000,
141
+ },
142
+ {
143
+ environment: 'Node',
144
+ value: 'const output = parseInt(input)*2;',
145
+ timeoutMs: 5000,
146
+ },
147
+ ] as ProcessingSpecifications;
148
+
149
+ const result = await postProcessResponseV1({ price: 1000 }, postProcessingSpecifications, parameters);
155
150
 
156
151
  expect(result).toBe(4000);
157
152
  });
@@ -159,48 +154,43 @@ describe(postProcessApiCallResponse.name, () => {
159
154
  it('demonstrates access to endpointParameters, but reserved parameters are inaccessible', async () => {
160
155
  const myMultiplier = 10;
161
156
  const parameters = { _type: 'int256', _path: 'price', myMultiplier };
162
- const endpoint = createEndpoint({
163
- postProcessingSpecifications: [
164
- {
165
- environment: 'Node',
166
- value: `
157
+ const postProcessingSpecifications = [
158
+ {
159
+ environment: 'Node',
160
+ value: `
167
161
  const reservedMultiplier = endpointParameters._times ? 1 : 2;
168
162
  const output = parseInt(input.price) * endpointParameters.myMultiplier * reservedMultiplier
169
163
  `,
170
- timeoutMs: 5000,
171
- },
172
- ],
173
- });
164
+ timeoutMs: 5000,
165
+ },
166
+ ] as ProcessingSpecifications;
174
167
 
175
168
  const price = 1000;
176
- const result = await postProcessApiCallResponse({ price }, endpoint, parameters);
169
+ const result = await postProcessResponseV1({ price }, postProcessingSpecifications, parameters);
177
170
 
178
- // reserved parameters (_times) should be inaccessible to post-processing for the
179
- // http-gateway, hence multiplication by 2 instead of 1
171
+ // reserved parameters (_times) should be inaccessible to post-processing hence multiplication by 2 instead of 1
180
172
  expect(result).toEqual(price * myMultiplier * 2);
181
173
  });
182
174
 
183
175
  it('throws on invalid code', async () => {
184
176
  const parameters = { _type: 'int256', _path: 'price' };
185
- const endpoint = createEndpoint({
186
- postProcessingSpecifications: [
187
- {
188
- environment: 'Node',
189
- value: 'const output = parseInt(input.price)*1000;',
190
- timeoutMs: 5000,
191
- },
192
- {
193
- environment: 'Node',
194
- value: `
177
+ const postProcessingSpecifications = [
178
+ {
179
+ environment: 'Node',
180
+ value: 'const output = parseInt(input.price)*1000;',
181
+ timeoutMs: 5000,
182
+ },
183
+ {
184
+ environment: 'Node',
185
+ value: `
195
186
  Something Unexpected;
196
187
  const output = parseInt(input)*2;
197
188
  `,
198
- timeoutMs: 5000,
199
- },
200
- ],
201
- });
189
+ timeoutMs: 5000,
190
+ },
191
+ ] as ProcessingSpecifications;
202
192
 
203
- const throwingFunc = async () => postProcessApiCallResponse({ price: 1000 }, endpoint, parameters);
193
+ const throwingFunc = async () => postProcessResponseV1({ price: 1000 }, postProcessingSpecifications, parameters);
204
194
 
205
195
  await expect(throwingFunc).rejects.toEqual(new Error('SyntaxError: Unexpected identifier'));
206
196
  });
@@ -270,3 +260,331 @@ describe(addReservedParameters.name, () => {
270
260
  expect(result).toEqual(modifiedParameters);
271
261
  });
272
262
  });
263
+
264
+ describe(preProcessEndpointParametersV2.name, () => {
265
+ describe('migration from v1 processing', () => {
266
+ it('valid processing code', async () => {
267
+ const preProcessingSpecificationV2 = {
268
+ environment: 'Node',
269
+ value: `
270
+ async (payload) => {
271
+ const { endpointParameters } = payload;
272
+ return { endpointParameters: {...endpointParameters, from: 'ETH', newProp: 'airnode'} };
273
+ }
274
+ `,
275
+ timeoutMs: 5000,
276
+ } as ProcessingSpecificationV2;
277
+ const parameters = { _type: 'int256', _path: 'price' };
278
+
279
+ const result = await preProcessEndpointParametersV2(preProcessingSpecificationV2, parameters);
280
+
281
+ expect(result).toEqual({
282
+ endpointParameters: {
283
+ _path: 'price',
284
+ _type: 'int256',
285
+ from: 'ETH',
286
+ newProp: 'airnode',
287
+ },
288
+ });
289
+ });
290
+
291
+ it('invalid processing code', async () => {
292
+ const preProcessingSpecificationV2 = {
293
+ environment: 'Node',
294
+ value: 'something invalid; const output = {...input, from: `ETH`};',
295
+ timeoutMs: 5000,
296
+ } as ProcessingSpecificationV2;
297
+ const parameters = { _type: 'int256', _path: 'price', from: 'TBD' };
298
+
299
+ const throwingFunc = async () => preProcessEndpointParametersV2(preProcessingSpecificationV2, parameters);
300
+
301
+ await expect(throwingFunc).rejects.toEqual(new Error('SyntaxError: Unexpected identifier'));
302
+ });
303
+
304
+ it('reserved parameters are inaccessible', async () => {
305
+ const parameters = { _type: 'int256', _path: 'price', to: 'USD' };
306
+ const preProcessingSpecificationV2 = {
307
+ environment: 'Node',
308
+ // pretend the user is trying to 1) override _path and 2) set a new parameter based on
309
+ // the presence of the reserved parameter _type (which is inaccessible)
310
+ value: `
311
+ async ({endpointParameters}) => {
312
+ return {endpointParameters: {...endpointParameters, from: "ETH", _path: "price.newpath", myVal: endpointParameters._type ? "123" : "456", newTo: endpointParameters.to } };
313
+ }
314
+ `,
315
+ timeoutMs: 5000,
316
+ } as ProcessingSpecificationV2;
317
+
318
+ const result = await preProcessEndpointParametersV2(preProcessingSpecificationV2, parameters);
319
+
320
+ expect(result).toEqual({
321
+ endpointParameters: {
322
+ _path: 'price', // is not overridden
323
+ _type: 'int256',
324
+ from: 'ETH', // originates from the processing code
325
+ to: 'USD', // should be unchanged from the original parameters
326
+ myVal: '456', // is set to "456" because _type is not present in the environment
327
+ newTo: 'USD', // demonstrates access to endpointParameters
328
+ },
329
+ });
330
+ });
331
+
332
+ it('uses native modules for processing', async () => {
333
+ const preProcessingSpecificationV2 = {
334
+ environment: 'Node',
335
+ value: `
336
+ async ({endpointParameters}) => {
337
+ const randomValue = crypto.randomBytes(4).toString('hex');
338
+ return {endpointParameters: {...endpointParameters, randomValue}};
339
+ }
340
+ `,
341
+ timeoutMs: 5000,
342
+ } as ProcessingSpecificationV2;
343
+ const parameters = { _type: 'int256', _path: 'price' };
344
+
345
+ const result = await preProcessEndpointParametersV2(preProcessingSpecificationV2, parameters);
346
+
347
+ // Check that the result contains the original parameters and a valid 8-character hex random value.
348
+ expect(result.endpointParameters).toEqual({
349
+ _path: 'price',
350
+ _type: 'int256',
351
+ randomValue: expect.any(String),
352
+ });
353
+ expect(/^[\da-f]{8}$/i.test(result.endpointParameters.randomValue)).toBe(true);
354
+ });
355
+
356
+ it('throws error due to processing timeout', async () => {
357
+ const preProcessingSpecificationV2 = {
358
+ environment: 'Node',
359
+ value: `
360
+ async ({endpointParameters}) => {
361
+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
362
+ await delay(5000);
363
+ return {endpointParameters: {...endpointParameters, from: 'ETH'}};
364
+ }`,
365
+ timeoutMs: 100, // This timeout is shorter than the delay in the processing code.
366
+ } as ProcessingSpecificationV2;
367
+ const parameters = { _type: 'int256', _path: 'price' };
368
+
369
+ const throwingFunc = async () => preProcessEndpointParametersV2(preProcessingSpecificationV2, parameters);
370
+
371
+ await expect(throwingFunc).rejects.toThrow('Full timeout exceeded');
372
+ });
373
+ });
374
+ });
375
+
376
+ describe(postProcessResponseV2.name, () => {
377
+ describe('migration from v1 processing', () => {
378
+ it('processes valid code', async () => {
379
+ const parameters = { _type: 'int256', _path: 'price' };
380
+ const postProcessingSpecificationV2 = {
381
+ environment: 'Node',
382
+ value: `
383
+ async (payload) => {
384
+ const { response } = payload;
385
+ return { response: parseInt(response.price) * 4 };
386
+ }
387
+ `,
388
+ timeoutMs: 5000,
389
+ } as ProcessingSpecificationV2;
390
+
391
+ const result = await postProcessResponseV2({ price: 1000 }, postProcessingSpecificationV2, parameters);
392
+
393
+ expect(result).toEqual({ response: 4000 });
394
+ });
395
+
396
+ it('throws when the code is of incorrect shape', async () => {
397
+ const parameters = { _type: 'int256', _path: 'price' };
398
+ const postProcessingSpecificationV2 = {
399
+ environment: 'Node',
400
+ value: `
401
+ async (payload) => {
402
+ const { response } = payload;
403
+ return parseInt(response.price) * 4;
404
+ }
405
+ `,
406
+ timeoutMs: 5000,
407
+ } as ProcessingSpecificationV2;
408
+
409
+ await expect(async () =>
410
+ postProcessResponseV2({ price: 1000 }, postProcessingSpecificationV2, parameters)
411
+ ).rejects.toEqual(
412
+ new ZodError([
413
+ {
414
+ code: 'invalid_type',
415
+ expected: 'object',
416
+ received: 'number',
417
+ path: [],
418
+ message: 'Expected object, received number',
419
+ },
420
+ ])
421
+ );
422
+ });
423
+
424
+ it('demonstrates access to endpointParameters, but reserved parameters are inaccessible', async () => {
425
+ const myMultiplier = 10;
426
+ const parameters = { _type: 'int256', _path: 'price', myMultiplier };
427
+ const postProcessingSpecificationV2 = {
428
+ environment: 'Node',
429
+ value: `
430
+ async (payload) => {
431
+ const {response, endpointParameters} = payload;
432
+ const reservedMultiplier = endpointParameters._times ? 1 : 2;
433
+ return {response: parseInt(response.price) * endpointParameters.myMultiplier * reservedMultiplier}
434
+ }
435
+ `,
436
+ timeoutMs: 5000,
437
+ } as ProcessingSpecificationV2;
438
+
439
+ const price = 1000;
440
+ const result = await postProcessResponseV2({ price }, postProcessingSpecificationV2, parameters);
441
+
442
+ // reserved parameters (_times) should be inaccessible to post-processing hence multiplication by 2 instead of 1
443
+ expect(result).toEqual({ response: price * myMultiplier * 2 });
444
+ });
445
+
446
+ it('throws on invalid code', async () => {
447
+ const parameters = { _type: 'int256', _path: 'price' };
448
+ const postProcessingSpecificationV2 = {
449
+ environment: 'Node',
450
+ value: 'Something Unexpected;',
451
+ timeoutMs: 5000,
452
+ } as ProcessingSpecificationV2;
453
+
454
+ const throwingFunc = async () =>
455
+ postProcessResponseV2({ price: 1000 }, postProcessingSpecificationV2, parameters);
456
+
457
+ await expect(throwingFunc).rejects.toEqual(new Error('SyntaxError: Unexpected identifier'));
458
+ });
459
+ });
460
+
461
+ it('can post-process timestamp', async () => {
462
+ const parameters = { _type: 'int256', _path: 'price' };
463
+ const postProcessingSpecificationV2 = {
464
+ environment: 'Node',
465
+ value: `
466
+ async (payload) => {
467
+ const { response } = payload;
468
+ return { response, timestamp: response.timestamp };
469
+ }
470
+ `,
471
+ timeoutMs: 5000,
472
+ } as ProcessingSpecificationV2;
473
+
474
+ const currentTimestamp = Math.floor(Date.now() / 1000);
475
+ const result1 = await postProcessResponseV2(
476
+ { price: 1000, timestamp: currentTimestamp },
477
+ postProcessingSpecificationV2,
478
+ parameters
479
+ );
480
+ expect(result1).toEqual({
481
+ response: { price: 1000, timestamp: currentTimestamp },
482
+ timestamp: currentTimestamp,
483
+ });
484
+
485
+ const result2 = await postProcessResponseV2({ price: 1000 }, postProcessingSpecificationV2, parameters);
486
+ expect(result2).toEqual({ response: { price: 1000 }, timestamp: undefined });
487
+ });
488
+ });
489
+
490
+ describe(preProcessEndpointParameters.name, () => {
491
+ it('returns v2 processing result', async () => {
492
+ const endpoint = createEndpoint({
493
+ preProcessingSpecificationV2: {
494
+ environment: 'Node',
495
+ value: `
496
+ async (payload) => {
497
+ const { endpointParameters } = payload;
498
+ return { endpointParameters: {...endpointParameters, from: 'ETH', newProp: 'airnode'} };
499
+ }
500
+ `,
501
+ timeoutMs: 5000,
502
+ },
503
+ });
504
+ const parameters = { _type: 'int256', _path: 'price' };
505
+
506
+ const result = await preProcessEndpointParameters(endpoint, parameters);
507
+
508
+ expect(result).toEqual({
509
+ endpointParameters: {
510
+ _path: 'price',
511
+ _type: 'int256',
512
+ from: 'ETH',
513
+ newProp: 'airnode',
514
+ },
515
+ });
516
+ });
517
+
518
+ it('converts v1 pre-processing to v2', async () => {
519
+ const endpoint = createEndpoint({
520
+ preProcessingSpecifications: [
521
+ {
522
+ environment: 'Node',
523
+ value: 'const output = {...input, from: "ETH"};',
524
+ timeoutMs: 5000,
525
+ },
526
+ {
527
+ environment: 'Node',
528
+ value: 'const output = {...input, newProp: "airnode"};',
529
+ timeoutMs: 5000,
530
+ },
531
+ ],
532
+ });
533
+ const parameters = { _type: 'int256', _path: 'price' };
534
+
535
+ const result = await preProcessEndpointParameters(endpoint, parameters);
536
+
537
+ expect(result).toEqual({
538
+ endpointParameters: {
539
+ _path: 'price',
540
+ _type: 'int256',
541
+ from: 'ETH',
542
+ newProp: 'airnode',
543
+ },
544
+ });
545
+ });
546
+ });
547
+
548
+ describe(postProcessResponse.name, () => {
549
+ it('returns v2 processing result', async () => {
550
+ const parameters = { _type: 'int256', _path: 'price' };
551
+ const endpoint = createEndpoint({
552
+ postProcessingSpecificationV2: {
553
+ environment: 'Node',
554
+ value: `
555
+ async (payload) => {
556
+ const { response } = payload;
557
+ return { response: parseInt(response.price) * 4 };
558
+ }
559
+ `,
560
+ timeoutMs: 5000,
561
+ },
562
+ });
563
+
564
+ const result = await postProcessResponse({ price: 1000 }, endpoint, parameters);
565
+
566
+ expect(result).toEqual({ response: 4000 });
567
+ });
568
+
569
+ it('converts v1 post-processing to v2', async () => {
570
+ const parameters = { _type: 'int256', _path: 'price' };
571
+ const endpoint = createEndpoint({
572
+ postProcessingSpecifications: [
573
+ {
574
+ environment: 'Node',
575
+ value: 'const output = parseInt(input.price)*2;',
576
+ timeoutMs: 5000,
577
+ },
578
+ {
579
+ environment: 'Node',
580
+ value: 'const output = parseInt(input)*2;',
581
+ timeoutMs: 5000,
582
+ },
583
+ ],
584
+ });
585
+
586
+ const result = await postProcessResponse({ price: 1000 }, endpoint, parameters);
587
+
588
+ expect(result).toStrictEqual({ response: 4000 });
589
+ });
590
+ });