Skip to content

Commit

Permalink
Support processing v2
Browse files Browse the repository at this point in the history
  • Loading branch information
Siegrift committed Nov 18, 2023
1 parent 449fa39 commit 02d1525
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,11 @@
}
}
],
"postProcessingSpecifications": [
{
"environment": "Node",
"timeoutMs": 5000,
"value": "\n // Log out every coin data\n input.forEach((coinData) => {\n console.log(`[Post-processing snippet]: Received the following coin data: \\${JSON.stringify(coinData, null, 2)}`)\n })\n\n const sum = (nums) => nums.reduce((acc, num) => acc + num, 0);\n const average = sum(input.map((coinData) => coinData.current_price)) / input.length;\n const percentageChange = sum(input.map((coinData) => coinData.price_change_percentage_30d_in_currency)) / input.length;\n\n // Create the data to be sent on chain and multiply it by 10^8 to preserve precision\n const output = [average, percentageChange].map((x) => x * 10 ** 8);\n "
}
]
"postProcessingSpecificationV2": {
"environment": "Node async",
"timeoutMs": 5000,
"value": "async function({ apiCallResponse }) {\n // Log out every coin data\n apiCallResponse.forEach((coinData) => {\n console.log(`[Post-processing snippet]: Received the following coin data: \\${JSON.stringify(coinData, null, 2)}`);\n });\n\n const sum = (nums) => nums.reduce((acc, num) => acc + num, 0);\n const average = sum(apiCallResponse.map((coinData) => coinData.current_price)) / apiCallResponse.length;\n const percentageChange =\n sum(apiCallResponse.map((coinData) => coinData.price_change_percentage_30d_in_currency)) / apiCallResponse.length;\n\n // Create the data to be sent on chain and multiply it by 10^8 to preserve precision\n return { apiCallResponse: [average, percentageChange].map((x) => x * 10 ** 8) };\n}"
}
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,11 @@
}
}
],
"preProcessingSpecifications": [
{
"environment": "Node",
"timeoutMs": 5000,
"value": "\n const rawDate = new Date(input.unixTimestamp * 1000);\n const day = rawDate.getDate().toString().padStart(2, '0');\n const month = (rawDate.getMonth() + 1).toString().padStart(2, '0'); // Months start at 0\n const year = rawDate.getFullYear();\n\n const formattedDate = day + '-' + month + '-' + year;\n const output = {...input, unixTimestamp: formattedDate};\n\n console.log(`[Pre-processing snippet]: Formatted \\${input.unixTimestamp} to \\${formattedDate}.`)\n "
}
]
"preProcessingSpecificationV2": {
"environment": "Node async",
"timeoutMs": 5000,
"value": "async ({apiCallParameters}) => {\n const rawDate = new Date(apiCallParameters.unixTimestamp * 1000);\n const day = rawDate.getDate().toString().padStart(2, '0');\n const month = (rawDate.getMonth() + 1).toString().padStart(2, '0'); // Months start at 0\n const year = rawDate.getFullYear();\n\n const formattedDate = day + '-' + month + '-' + year;\n const newApiCallParameters = {...apiCallParameters, unixTimestamp: formattedDate};\n\n console.log(`[Pre-processing snippet]: Formatted \\${apiCallParameters.unixTimestamp} to \\${formattedDate}.`)\n return {apiCallParameters: newApiCallParameters};\n}"
}
}
]
}
Expand Down
4 changes: 2 additions & 2 deletions packages/airnode-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"@api3/airnode-protocol": "^0.12.0",
"@api3/airnode-utilities": "^0.12.0",
"@api3/airnode-validator": "^0.12.0",
"@api3/commons": "^0.3.0",
"@api3/ois": "2.2.1",
"@api3/commons": "file:../../../commons",
"@api3/ois": "file:../../../ois",
"@api3/promise-utils": "^0.4.0",
"@aws-sdk/client-lambda": "^3.418.0",
"date-fns": "^2.30.0",
Expand Down
34 changes: 20 additions & 14 deletions packages/airnode-node/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as adapter from '@api3/airnode-adapter';
import isEmpty from 'lodash/isEmpty';
import { RESERVED_PARAMETERS } from '@api3/ois';
import { OIS, RESERVED_PARAMETERS } from '@api3/ois';
import { preProcessApiCallParameters, postProcessApiCallResponse } from '@api3/commons';
import { logger, removeKeys, removeKey } from '@api3/airnode-utilities';
import { go, goSync } from '@api3/promise-utils';
Expand All @@ -27,7 +27,7 @@ export function buildOptions(payload: ApiCallPayload): adapter.BuildRequestOptio
const { type, config, aggregatedApiCall } = payload;
const { endpointName, parameters, oisTitle } = aggregatedApiCall;

const ois = config.ois.find((o) => o.title === oisTitle)!;
const ois = config.ois.find((o) => o.title === oisTitle)! as OIS;
const apiCredentials = config.apiCredentials
.filter((c) => c.oisTitle === oisTitle)
.map((c) => removeKey(c, 'oisTitle')) as adapter.BaseApiCredentials[];
Expand Down Expand Up @@ -217,7 +217,7 @@ export async function processSuccessfulApiCall(
): Promise<LogsData<ApiCallResponse>> {
const { type, config, aggregatedApiCall } = payload;
const { endpointName, oisTitle, parameters } = aggregatedApiCall;
const ois = config.ois.find((o) => o.title === oisTitle)!;
const ois = config.ois.find((o) => o.title === oisTitle)! as OIS;
const endpoint = ois.endpoints.find((e) => e.name === endpointName)!;
// _minConfirmations is handled prior to the API call
const { _type, _path, _times, _gasPrice } = getReservedParameters(endpoint, parameters);
Expand All @@ -231,9 +231,10 @@ export async function processSuccessfulApiCall(
const log = logger.pend('ERROR', goPostProcessApiSpecifications.error.message);
return [[log], { success: false, errorMessage: goPostProcessApiSpecifications.error.message }];
}
const postProcessedValue = goPostProcessApiSpecifications.data.apiCallResponse;

const goExtractAndEncodeResponse = goSync(() =>
adapter.extractAndEncodeResponse(goPostProcessApiSpecifications.data, {
adapter.extractAndEncodeResponse(postProcessedValue, {
_type,
_path,
_times,
Expand Down Expand Up @@ -273,7 +274,7 @@ export async function processSuccessfulApiCall(
];
}
case 'http-signed-data-gateway': {
const timestamp = Math.floor(Date.now() / 1000).toString();
const timestamp = (goPostProcessApiSpecifications.data.timestamp ?? Math.floor(Date.now() / 1000)).toString();
const goSignWithTemplateId = await go(() =>
signWithTemplateId(aggregatedApiCall.templateId, timestamp, response.encodedValue)
);
Expand All @@ -300,28 +301,33 @@ export async function callApi(payload: ApiCallPayload): Promise<LogsData<ApiCall
const {
aggregatedApiCall: { parameters },
} = payload;
const ois = payload.config.ois.find((o) => o.title === payload.aggregatedApiCall.oisTitle)!;
const ois = payload.config.ois.find((o) => o.title === payload.aggregatedApiCall.oisTitle)! as OIS;
const endpoint = ois.endpoints.find((e) => e.name === payload.aggregatedApiCall.endpointName)!;
const processedParameters = await preProcessApiCallParameters(endpoint, parameters, {
const { apiCallParameters: processedApiCallParameters } = await preProcessApiCallParameters(endpoint, parameters, {
totalTimeoutMs: PROCESSING_TIMEOUT,
});

// skip API call if operation is undefined and fixedOperationParameters is empty array
if (!endpoint.operation && isEmpty(endpoint.fixedOperationParameters)) {
// contents of preProcessingSpecifications or postProcessingSpecifications (or both) will simulate an API when API call is skipped
if (isEmpty(endpoint.preProcessingSpecifications) && isEmpty(endpoint.postProcessingSpecifications)) {
const message = `Failed to skip API call. Ensure at least one of 'preProcessingSpecifications' or 'postProcessingSpecifications' is defined and is not an empty array at ois '${payload.aggregatedApiCall.oisTitle}', endpoint '${payload.aggregatedApiCall.endpointName}'.`;
// Some processing needs to be defined.
if (
isEmpty(endpoint.preProcessingSpecifications) &&
isEmpty(endpoint.postProcessingSpecifications) &&
!endpoint.preProcessingSpecificationV2 &&
!endpoint.postProcessingSpecificationV2
) {
const message = `Failed to skip API call. Ensure at least one of 'preProcessingSpecifications' or 'postProcessingSpecifications' is defined and is not an empty array or define 'preProcessingSpecificationV2' or 'postProcessingSpecificationV2' at ois '${payload.aggregatedApiCall.oisTitle}', endpoint '${payload.aggregatedApiCall.endpointName}'.`;
const log = logger.pend('ERROR', message);
return [[log], { success: false, errorMessage: message }];
}
// output of preProcessingSpecifications can be used as output directly or
// preProcessingSpecifications can be used to manipulate parameters to use in postProcessingSpecifications
return processSuccessfulApiCall(payload, { data: processedParameters });
// The pre-processing output can be used as output directly or it can be used to manipulate parameters to use in
// post-processing.
return processSuccessfulApiCall(payload, { data: processedApiCallParameters });
}

const [logs, response] = await performApiCall({
...payload,
aggregatedApiCall: { ...payload.aggregatedApiCall, parameters: processedParameters },
aggregatedApiCall: { ...payload.aggregatedApiCall, parameters: processedApiCallParameters },
} as ApiCallPayload);
if (isPerformApiCallFailure(response)) {
return [logs, response];
Expand Down
4 changes: 0 additions & 4 deletions packages/airnode-node/src/cli/http-signed-data-invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,6 @@ yargs(hideBin(process.argv))
async (args) => {
logger.log(`Invoke HTTP signed data handler`);
const config = loadTrustedConfig(path.resolve(`${__dirname}/../../config/config.json`), process.env);
const parameters = JSON.parse(args.encodedParameters);
if (!parameters) {
throw new Error('Missing request parameters');
}
logger.log(
JSON.stringify(await processHttpSignedDataRequest(config, args['endpoint-id'], args['encoded-parameters']))
);
Expand Down
19 changes: 12 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@
dependencies:
zod "^3.22.2"

"@api3/commons@^0.3.0":
version "0.3.0"
resolved "https://registry.npmjs.org/@api3/commons/-/commons-0.3.0.tgz#846dace14f8c1abed5df1a3e5415e8b3a28cfa21"
integrity sha512-27+UkW0qCWvQCJuO8vbyBhJ94IXELKhqCKjFxC5ZUQrJHRHWT4zz2wmdbsvgUHBZhuAuFPaz/78yjMCjzJ6S8Q==
"@api3/commons@file:../commons":
version "0.4.1"
dependencies:
"@api3/ois" "^2.2.1"
"@api3/ois" "file:../../../Library/Caches/Yarn/v6/npm-@api3-commons-0.4.1-a70bb46a-66f0-4239-8006-f2b03c1f06e5-1700321512734/node_modules/@api3/ois"
"@api3/promise-utils" "^0.4.0"
"@typescript-eslint/eslint-plugin" "^6.2.1"
"@typescript-eslint/parser" "^6.2.1"
Expand All @@ -55,15 +53,22 @@
lodash "^4.17.21"
winston "^3.10.0"
winston-console-format "^1.0.8"
zod "^3.22.4"

"@api3/[email protected]", "@api3/ois@^2.2.1":
"@api3/[email protected]":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@api3/ois/-/ois-2.2.1.tgz#caa5c672c51b5572cf9bb30226a0647b3a51a57d"
integrity sha512-C4tSMBccDlD8NkZMVATQXOKctI46fSOlzpbZmZoFknsIdYfQvGNU49StGRJZ6fJJkwXEX1TlkRC7rY2yHEJjqw==
dependencies:
lodash "^4.17.21"
zod "^3.22.3"

"@api3/ois@file:../ois":
version "2.2.1"
dependencies:
lodash "^4.17.21"
zod "^3.22.4"

"@api3/promise-utils@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@api3/promise-utils/-/promise-utils-0.4.0.tgz#d1dcd77d74377b4fdb3071d2cc76d98c9151309c"
Expand Down Expand Up @@ -16020,7 +16025,7 @@ zod@^3.22.2:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.2.tgz#3add8c682b7077c05ac6f979fea6998b573e157b"
integrity sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==

zod@^3.22.3:
zod@^3.22.3, zod@^3.22.4:
version "3.22.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==

0 comments on commit 02d1525

Please sign in to comment.