Chainlink External Adapter with RapidAPI [Request failed with 500 Internal Server Error] - chainlink

I followed chainlink tutorial to build an external adapter. Unfortunately, after hours of debugging, my EA is still not working, flashing the 500 internal server error.
HTTP/1.1 500 Internal Server Error
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 186
ETag: W/"ba-z+FLILUhYUEw92/8nbj21rBflVg"
Date: Wed, 11 May 2022 05:05:45 GMT
Connection: close
{
"jobRunID": 0,
"status": "errored",
"error": {
"error": {
"name": "AdapterError",
"message": {
"error": {
"name": "AdapterError",
"message": "Required parameter not supplied: base"
}
}
}
},
"statusCode": 500
}
The API I am trying to fetch is AeroDataBox API (from RapidAPI marketplace). We need 2 params: flight number (AC1309) and date (2022-05-09).
This is the RapidAPI code snippet recommendation:
const fetch = require('node-fetch');
const url = 'https://aerodatabox.p.rapidapi.com/flights/number/AC1039/2022-05-09';
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Host': 'aerodatabox.p.rapidapi.com',
'X-RapidAPI-Key': 'db0eaaf009msh506491d3d299ed8p10a8d4jsnc705a04d3eb2'
}
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error('error:' + err));
This is the snippet of my code in the index.js:
const customParams = {
flightNumber: ['flightNumber'],
date: ['yyyy-mm-dd'],
endpoint: false
}
const createRequest = (input, callback) => {
const validator = new Validator(callback, input, customParams)
const jobRunID = validator.validated.id
const API_KEY = process.env.API_KEY
const flightNumber = validator.validated.data.flightNumber
const date = validator.validated.data.date
const url = `https://aerodatabox.p.rapidapi.com/flights/number/${flightNumber}/${date}`
const params = {
flightNumber,
date
}
const headers = {
'X-RapidAPI-Host': 'aerodatabox.p.rapidapi.com',
'X-RapidAPI-Key': `${API_KEY}`
}
}
const config = {
url,
params,
headers
}
Requester.request(config, customError)
.then(response => {
callback(response.status, Requester.success(jobRunID, response.data))
})
.catch(error => {
callback(500, Requester.errored(jobRunID, error))
})
This is my curl command:
curl -X POST -H "content-type:application/json" "http://localhost:8080/" --data '{ "id": 0, "data": { "flightNumber": "AC1309", "date": "2022-05-09" } }'
I don't know what I am missing? Thank you for your help.

Related

Unable to store timestamp in Dynamo DB using Nodejs 16 version Lambda

I am unable to store timestamp in Dynamo DB using Node JS Lambda. i am new to Node JS.
while run the test case, it was stored. but while trigger this lambda function getting error.
i was try to insert below timestamp
LastUpdateTimestamp
The date and time this contact was last updated, in UTC time.
Type: String (yyyy-mm-ddThh:mm:ssZ)
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
date: { S: event.Details.ContactData.LastUpdateTimestamp},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};
Test Data:
{
"Name": "ContactFlowEvent",
"Details": {
"ContactData": {
"Attributes": {
"surveyId": "123456",
"survey_result_1": "4",
"survey_result_2": "5"
},
"Channel": "VOICE",
"ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"LastUpdateTimestamp": "2023-02-14T06:52:29Z",
"CustomerEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
},
"InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"InitiationMethod": "API",
"InstanceARN": "arn:aws:connect:us-east-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa",
"MediaStreams": {
"Customer": {
"Audio": {
"StartFragmentNumber": "91343852333181432392682062622220590765191907586",
"StartTimestamp": "1565781909613",
"StreamARN": "arn:aws:kinesisvideo:us-east-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806"
}
}
},
"PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe",
"Queue": null,
"SystemEndpoint": {
"Address": "+11234567890",
"Type": "TELEPHONE_NUMBER"
}
},
"Parameters": {}
}
}
Error:
2023-02-15T14:10:26.426Z 94fad25e-bcdb-443b-95c0-47640bfaba34 INFO ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes
at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14)
at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12)
at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
code: 'ValidationException',
time: 2023-02-15T14:10:26.328Z,
requestId: 'F5JJOCF20D507JCFRO7FEMAH6VVV4KQNSO5AEMVJF66Q9ASUAAJG',
statusCode: 400,
retryable: false,
retryDelay: 15.458319111471575
}
You state that ...surveyResults contains the following:
"survey_result_1": "4",
"survey_result_2": "5"
This is not DynamoDB JSON. You need to modify the values to suit the correct format as you have done with the other values.
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB();
exports.handler = async (event) => {
var surveyResults = {};
var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event
Object.keys(data).forEach(element => {
if (element.startsWith("survey_result_")) {
surveyResults[element] = { S: data[element] };
}
});
var contact_params = {
ContactId: event.Details.ContactData.InitialContactId, /* required */
InstanceId: 'place your instance id' /* required */
};
var connect = new AWS.Connect();
console.log('describeContact');
/*connect.describeContact(contact_params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});*/
const attributes = await connect.describeContact(contact_params).promise();
console.log(attributes)
console.log(typeof attributes.Contact.LastUpdateTimestamp)
var agent_params = {
InstanceId: 'place your instance id', /* required */
UserId: attributes.Contact.AgentInfo.Id /* required */
};
const agent = await connect.describeUser(agent_params).promise();
let agent_username = "";
if(agent_username != null){
agent_username = agent.User.Username;
}
console.log(agent)
var params = {
TableName: process.env.TABLE,
Item: {
contactId: { S: event.Details.ContactData.ContactId},
surveyId: { S: event.Details.ContactData.Attributes.surveyId },
date: { S: attributes.Contact.LastUpdateTimestamp.toLocaleDateString()},
time: { S: attributes.Contact.LastUpdateTimestamp.toLocaleTimeString()},
agentname: {S: agent_username},
...surveyResults
}
}
try {
await ddb.putItem(params).promise(); // write the object to the DynamoDB table
} catch (err) {
console.log(err);
}
const response = {
statusCode: 200,
body: JSON.stringify('OK'),
};
return response;
};

Using Nextjs (getServerSideprops) with elasticsearch from lambda node causes an error

I use nextjs with elastic search cloud, also use amplify and lambda created from amplify rest api.
This is my function:
export async function getServerSideProps(context) {
let esItems;
try {
const { query } = context.query;
const apiName = 'APINAME';
const path = '/searchlist';
const myInit = {
response: true,
queryStringParameters: { query: query }
};
const response = await API.get(apiName, path, myInit);
esItems = response;
} catch (err) {
console.log(err)
}
return {
props: {
allProducts: esItems ? esItems.data.items : [],
}
};
}
I return 50 products from elastic and get this error:
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
This is lambda function:
app.get('/searchlist', async (req, res) => {
const { query } = req.query;
const client = new Client({
node: "https://elastic-cloud....",
auth: {
username: process.env.USERNAME,
password: process.env.PASSWORD
}
});
const searchQuery = {
query: {
multi_match: {
query: query,
type: "phrase",
fields: [
'manufacturerTypeDescription^8',
'manufacturerName^6',
'ean^4',
'_id^2',
]
}
}
}
const typeSearch = {
index: "product",
size: 50,
body: searchQuery
}
const r = await client.search(typeSearch);
const hits = r.body.hits;
const items = hits.hits.map((hit) => ({
_id: hit._id,
...hit._source,
}))
res.json({
success: 'get call succeed!',
items
});
});

Why am I getting a 400 Bad request when calling Plaid's linkTokenCreate function?

I am attempting to set up Plaid in my app, and am following the Quickstart guide for Node.js and Express. When I call the client.linkTokenCreate function I am getting a status 400 Bad Request response. I believe my code exactly matches the quickstart, and I am using sandbox mode, so I am unsure where I am going wrong.
const { Configuration, PlaidApi, PlaidEnvironments, Products, CountryCode } = require("plaid");
const configuration = new Configuration({
basePath: PlaidEnvironments[process.env.PLAID_ENV],
baseOptions: {
headers: {
"PLAID-CLIENT-ID": process.env.PLAID_CLIENT_ID,
"PLAID-SECRET": process.env.PLAID_SECRET,
},
},
});
console.log(configuration)
const client = new PlaidApi(configuration);
router.post("/create_link_token", async (req, res) => {
// Get the client_user_id by searching for the current user
// const user = await User.find(...);
// const clientUserId = user.id;
const request = {
user: {
// This should correspond to a unique id for the current user.
client_user_id: "test123",
},
client_name: "Name Of App",
products: [Products.Auth],
language: "en",
webhook: 'https://app.com',
country_codes: [CountryCode.Us],
};
try {
console.log("request",process.env.PLAID_CLIENT_ID,process.env.PLAID_SECRET)
const createTokenResponse = await client.linkTokenCreate(request);
console.log("createTokenResponse", createTokenResponse);
res.status(200).json(createTokenResponse);
} catch (error) {
console.log("error", error.message)
res.send(error.message)
}
});

NestJs Socket io adapter: Server does not add CORS headers to handshake's response if allowFunction is called with false. Bug or misconfiguration?

authenticated-socket-io.adapter.ts
export class AuthenticatedSocketIoAdapter extends IoAdapter {
private readonly authService: AuthService;
constructor(private app: INestApplicationContext) {
super(app);
this.authService = this.app.get(AuthService);
}
createIOServer(port: number, options?: SocketIO.ServerOptions): any {
options.allowRequest = async (request, allowFunction) => {
const { authorized, errorMessage } = await this.check(parse(request?.headers?.cookie || '').jwt, [UserRole.ADMIN]);
if (!authorized) {
return allowFunction(errorMessage, false);
}
return allowFunction(null, true);
};
return super.createIOServer(port, options);
}
main.ts
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: ['http://localhost:4200'],
credentials: true
});
app.use(cookieParser());
app.use(csurf({ cookie: true }));
app.useWebSocketAdapter(new AuthenticatedSocketIoAdapter(app));
When authorization is successful:
authorization is successful
When authorization fails:
authorization fails
Found the problem:
the function from socket io handling the error message:
/**
* Sends an Engine.IO Error Message
*
* #param {http.ServerResponse} response
* #param {code} error code
* #api private
*/
function sendErrorMessage (req, res, code) {
var headers = { 'Content-Type': 'application/json' };
var isForbidden = !Server.errorMessages.hasOwnProperty(code);
if (isForbidden) {
res.writeHead(403, headers);
res.end(JSON.stringify({
code: Server.errors.FORBIDDEN,
message: code || Server.errorMessages[Server.errors.FORBIDDEN]
}));
return;
}
if (req.headers.origin) {
headers['Access-Control-Allow-Credentials'] = 'true';
headers['Access-Control-Allow-Origin'] = req.headers.origin;
} else {
headers['Access-Control-Allow-Origin'] = '*';
}
if (res !== undefined) {
res.writeHead(400, headers);
res.end(JSON.stringify({
code: code,
message: Server.errorMessages[code]
}));
}
}
here the "code" param will be the one I pass in here "allowFunction(errorMessage, false)"
That value has to be one of "0", "1", "2", "3" or "4", otherwise the isForbidden will be false, thus not setting the 'Access-Control-Allow-Credentials' header.
Server.errorMessages = {
0: 'Transport unknown',
1: 'Session ID unknown',
2: 'Bad handshake method',
3: 'Bad request',
4: 'Forbidden'
};
Hope this helps someone one day.

Lambda and API gateway mapping

I want to return a value from handler to API gateway response header.
Handler.js
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
body: {
message: 'test'
},
header: {
Last-Modified: UpdateDate
}
});
};
s-function.json in "endpoints"
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.body.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
This can work. But I want to know how to use "integration.response.header.Last-Modified". Is my handler callback formate wrong?
Edit:
s-function.json in "endpoints"
"integration.response.header.Last-Modified" This doesn't work.
I want to know specific handler return formate to pass data to "integration.response.header.Last-Modified".
"responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Cache-Control": "'public, max-age=86400'",
"method.response.header.Last-Modified": "integration.response.header.Last-Modified"
},
"responseModels": {
"application/json;charset=UTF-8": "Empty"
},
"responseTemplates": {
"application/json;charset=UTF-8": "$input.json('$.body')"
}
}
}
All of the output from your lambda function is returned in the response body, so you will need to map part of the response body to your API response header.
module.exports.handler = function(event, context, cb) {
const UpdateDate = new Date();
return cb(null, {
message: 'test',
Last-Modified: UpdateDate
});
};
will produce payload "{"message" : "test", "Last-Modified" : "..."}"
In this case you would use "integration.response.body.Last-Modified" as the mapping expression. As a side note, naming things "body" and "header" in your response body may make the mapping expressions confusing to read.
Thanks,
Ryan

Resources