@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.
- package/README.md +2 -0
- package/dist/blockchain-utilities/derivation.d.ts +78 -0
- package/dist/blockchain-utilities/derivation.d.ts.map +1 -0
- package/dist/blockchain-utilities/derivation.js +97 -0
- package/dist/blockchain-utilities/derivation.js.map +1 -0
- package/dist/blockchain-utilities/schema.d.ts +12 -0
- package/dist/blockchain-utilities/schema.d.ts.map +1 -0
- package/dist/blockchain-utilities/schema.js +13 -0
- package/dist/blockchain-utilities/schema.js.map +1 -0
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/processing/processing.d.ts +70 -18
- package/dist/processing/processing.d.ts.map +1 -1
- package/dist/processing/processing.js +145 -37
- package/dist/processing/processing.js.map +1 -1
- package/dist/processing/schema.d.ts +25 -2
- package/dist/processing/schema.d.ts.map +1 -1
- package/dist/processing/schema.js +10 -9
- package/dist/processing/schema.js.map +1 -1
- package/dist/processing/unsafe-evaluate.d.ts +1 -0
- package/dist/processing/unsafe-evaluate.d.ts.map +1 -1
- package/dist/processing/unsafe-evaluate.js +31 -1
- package/dist/processing/unsafe-evaluate.js.map +1 -1
- package/package.json +5 -3
- package/src/blockchain-utilities/README.md +5 -0
- package/src/blockchain-utilities/derivation.test.ts +147 -0
- package/src/blockchain-utilities/derivation.ts +116 -0
- package/src/blockchain-utilities/schema.test.ts +23 -0
- package/src/blockchain-utilities/schema.ts +14 -0
- package/src/logger/index.test.ts +16 -0
- package/src/logger/index.ts +8 -5
- package/src/processing/README.md +89 -14
- package/src/processing/processing.test.ts +429 -111
- package/src/processing/processing.ts +196 -47
- package/src/processing/schema.ts +21 -8
- package/src/processing/unsafe-evaluate.test.ts +220 -1
- 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
|
-
|
|
7
|
-
|
|
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(
|
|
18
|
+
describe(preProcessEndpointParametersV1.name, () => {
|
|
12
19
|
it('valid processing code', async () => {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 () =>
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
97
|
+
timeoutMs: 5000,
|
|
98
|
+
},
|
|
99
|
+
] as ProcessingSpecifications;
|
|
101
100
|
const parameters = { _type: 'int256', _path: 'price' };
|
|
102
101
|
|
|
103
|
-
const result = await
|
|
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
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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 () =>
|
|
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(
|
|
133
|
+
describe(postProcessResponseV1.name, () => {
|
|
137
134
|
it('processes valid code', async () => {
|
|
138
135
|
const parameters = { _type: 'int256', _path: 'price' };
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
164
|
+
timeoutMs: 5000,
|
|
165
|
+
},
|
|
166
|
+
] as ProcessingSpecifications;
|
|
174
167
|
|
|
175
168
|
const price = 1000;
|
|
176
|
-
const result = await
|
|
169
|
+
const result = await postProcessResponseV1({ price }, postProcessingSpecifications, parameters);
|
|
177
170
|
|
|
178
|
-
// reserved parameters (_times) should be inaccessible to post-processing
|
|
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
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
189
|
+
timeoutMs: 5000,
|
|
190
|
+
},
|
|
191
|
+
] as ProcessingSpecifications;
|
|
202
192
|
|
|
203
|
-
const throwingFunc = async () =>
|
|
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
|
+
});
|