I'm just starting with Kong and setup a Lambda plugin on a service to try things out. The Lambda function I use had a simple method to parse the JSON body:
const getBody = (event: any): IBody => {
const body = JSON.parse(event.body)
return new Body(body)
}
So, although I was able to call the function and get a response from it, all I got was an error message similar to:
{"status":500,"message":"SyntaxError: Unexpected token u in JSON
at position 0"}
This is due the fact a Lambda request is different when invoked from the cli and when called from AWS API Gateway.
Basically event.body is only available when calling from the API Gateway, whilst when called from the cli, the correct property name is event.request_body.
So modifiying the method to the one below will allow me to receive calls both from AWS API Gateway and cli:
const getBody = (event: any): IBody => {
const body = JSON.parse(Object.is(event.request_body, undefined) ? event.body : event.request_body)
return new Body(body)
}
Related
const df = require("durable-functions");
module.exports = async function (context, req) {
const client = df.getClient(context);
context.log(`Function Name = '${req.params.functionName}'.`);
context.log(`Body = '${req.body}'.`);
const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
context.log(`Started orchestration with ID = '${instanceId}'.`);
return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};
I have tried to use POSTMAN or https://reqbin.com/ for testing but I always get object.
It is a simple case but I don't understand why it is not JSON object.
I read this one
TypeScript Azure Function Read Body of POST method as JSON
but it didn't help me.
Everything is fine!
It is already deserialized json object.
It looks like the normal string in a log after
JSON.stringify(req.body)
My fault was that I wrongly got it in the orchestrator function from the context.
I started implementing a slash-command which kept evolving and eventually might hit the 3-second slack response limit. I am using serverless-stack with Node and TypeScript. With sst (and the vscode launchfile) it hooks and attaches the debugger into the lambda function which is pretty neat for debugging.
When hitting the api endpoint I tried various methods to send back an acknowledgement to slack, do my thing and send a delayed message back without success. I didnt have much luck finding info on this but one good source was this SO Answer - unfortunetly it didn't work. I didn't use request-promise since it's deprecated and tried to implement it with vanilla methods (maybe that's where i failed?). But also invoking a second lambda function from within (like in the first example of the post) didn't seem to be within the 3s limitation.
I am wondering if I am doing something wrong or if attachinf the debugger is just taking to long etc.
However, before attempting to send a delayed message it was fine including accessing and scaning dynamodb records, manipulating the results and then responding back to slack while debugger attached without hitting the timeout.
Attempting to use a post
export const answer: APIGatewayProxyHandlerV2 = async (
event: APIGatewayProxyEventV2, context, callback
) => {
const slack = decodeQueryStringAs<SlackRequest>(event.body);
axios.post(slack.response_url, {
text: "completed",
response_type: "ephemeral",
replace_original: "true"
});
return { statusCode: 200, body: '' };
}
The promise never resolved, i guess that once hitting return on the function the lambda function gets disposed and so the promise?
Invoking 2nd Lambda function
export const v2: APIGatewayProxyHandlerV2 = async (
event: APIGatewayProxyEventV2, context, callback
): Promise<any> => {
//tried with CB here and without
//callback(null, { statusCode: 200, body: 'processing' });
const slack = decodeQueryStringAs<SlackRequest>(event.body);
const originalMessage = slack.text;
const responseInfo = url.parse(slack.response_url)
const data = JSON.stringify({
...slack,
})
const lambda = new AWS.Lambda()
const params = {
FunctionName: 'dev-****-FC******SmE7',
InvocationType: 'Event', // Ensures asynchronous execution
Payload: data
}
return lambda.invoke(params).promise()// Returns 200 immediately after invoking the second lambda, not waiting for the result
.then(() => callback(null, { statusCode: 200, body: 'working on it' }))
};
Looking at the debugger logs it does send the 200 code and invokes the new lambda function though slack still times out.
Nothing special happens logic wise ... the current non-delayed-message implementation does much more logic wise (accessing DB and manipulating result data) and manages not to timeout.
Any suggestions or help is welcome.
Quick side note, I used request-promise in the linked SO question's answer since the JS native Promise object was not yet available on AWS Lambda's containers at the time.
There's a fundamental difference between the orchestration of the functions in the linked question and your own from what I understand but I think you have the same goal:
> Invoke an asynchronous operation from Slack which posts back to slack once it has a result
Here's the problem with your current approach: Slack sends a request to your (1st) lambda function, which returns a response to slack, and then invokes the second lambda function.
The slack event is no longer accepting responses once your first lambda returns the 200. Here lies the difference between your approach and the linked SO question.
The desired approach would sequentially look like this:
Slack sends a request to Lambda no. 1
Lambda no. 1 returns a 200 response to Slack
Lambda no. 1 invokes Lambda no. 2
Lambda no. 2 sends a POST request to a slack URL (google incoming webhooks for slack)
Slack receives the POST requests and displays it in the channel you chose for your webhook.
Code wise this would look like the following (without request-promise lol):
Lambda 1
module.exports = async (event, context) => {
// Invoking the second lambda function
const AWS = require('aws-sdk')
const lambda = new AWS.Lambda()
const params = {
FunctionName: 'YOUR_SECOND_FUNCTION_NAME',
InvocationType: 'Event', // Ensures asynchronous execution
Payload: JSON.stringify({
... your payload for lambda 2 ...
})
}
await lambda.invoke(params).promise() // Starts Lambda 2
return {
text: "working...",
response_type: "ephemeral",
replace_original: "true"
}
}
Lambda 2
module.exports = async (event, context) => {
// Use event (payload sent from Lambda 1) and do what you need to do
return axios.post('YOUR_INCOMING_WEBHOOK_URL', {
text: 'this will be sent to slack'
});
}
I am trying to get some data via Webhook into lambda function using API Gateway and SQS. This Webhook contains a JSON payload, and HTTP headers that provide context. Since there is no way to get these webhook HTTP headers from API Gateway to SQS using "Action=SendMessage" in Mapping template (correct me if my assumption is wrong...I have tried http headers in method request and integration request), I have included the headers from webhook in the API Gateway mapping template as follows:
Action=SendMessage&MessageGroupId=$input.params('MessageGroupId')&
MessageBody={
"Header1":"$input.params('Header1 key')",
"body":"$input.json('$')"
}
My code in Lambda function is as follows:
'use strict';
exports.handler = async function(event, context) {
if (event.Records) {
event.Records.forEach(record => {
var rec = JSON.parse(JSON.stringify(record.body));
console.log("Record body: " + rec);
console.log("Header1: " + rec.Header1);
});
}
return {};
};
The value of 'rec' in CloudWatch log is rather quite long but here is the simplified version:
Record body: {
"Header1":" OYqmDnuUpDs8oF1RwoBnJywBY6c1I4qLklU=",
"body":"{"id":820982911946154508,"email":"jon#doe.ca","closed_at":null,"created_at":"2020-11-30T20:25:43-05:00","updated_at":"2020-11-30T20:25:43-05:00","number":234,"note":null,"token":"123456abcd","gateway":null,"test":true,"total_price":"7.00","subtotal_price":"-3.00"
}
My question - how can I read the values of Header1 and body? I have tried rec.Header1, rec[0].Header1 and rec["Header1"] etc. and I get "Undefined" in all the cases. Thanks.
The body is already stringified.
You need to simply call JSON.parse(record.body) to parse the string to valid JSON. Note that this could throw an error.
I have this an URL, let's assume, "www.sample.com/hello". Now I have triggered a lambda function on viewer request where I just need to change the url to "www.sample.com/hello2". I did it using lambda edge functions but it is throwing me an error.
This is the code I wrote in lamda
const path = require('path');
exports.handler = (event, context, callback) => {
const cf = event.Records[0].cf;
const request = cf.request;
const response = cf.response;
const statusCode = response.status;
const path = request.uri;
const afterpath = path.substring(path.indexOf("/")+1);
if (afterpath == 'sample') {
request.uri = request.uri
.replace(afterpath,'samplepathitis')
}
return callback(null, request);
};
I am getting this error
503 ERROR
The request could not be satisfied.
The Lambda function associated with the CloudFront distribution is invalid
or doesn't have the required permissions.
If you received this error while trying to use an app or access a website,
please contact the provider or website owner for assistance.
If you provide content to customers through CloudFront, you can find steps
to troubleshoot and help prevent this error by following steps in the
CloudFront documentation
(http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-
503-service-unavailable.html).
Generated by cloudfront (CloudFront)
Request ID: MsN6aG8qvI9ttt3_VLhQAqpY8kF2pHk3V095lAFVU_sWmDvF3IfqAA==
As the error message states it clearly : either the function is invalid or there is no permission to call the function.
To check if the function is valid : try to invoke it from the Lamba console. Use the Test button. You will need to pass a request as input. The console will propose you sample request that you can adjust to simulate your use case.
Also very in the doc the return value of the function. Is request the correct return value expected by Cloudfront ?
Once you are sure about the two above, verify the permission to invoke that function. What is the trigger ? Is Cloudfront authorized to invoke your function ?
Im using Firebase cloud functions. On an iOS device, im deploying a trigger to run a cloud function in Node.Js. In Xcode - here is my client function to trigger the Cloud function - I'm passing over the dictionary data
func updateTermsOfServiceCloudCall(){
let data = [
"accountId": "acct_1Ew#######"
]
Functions.functions().httpsCallable("updateAccountWithTOA").call(data) { (result, error) in
}
Now in Node, i'm running this code to deploy to the Firebase cloud
exports.updateAccountWithTOA = functions.https.onRequest((request, response) => {
const data = request.body;
const accoundId = data.accoundId;
stripe.accounts.update(
accoundId,
{
tos_acceptance: {
date: Math.floor(Date.now() / 1000),
ip: request.ip
}
},
)
});
I'm expecting to get the dictionary data that i passed over from my iOS client. However, im having an issue getting that data in Node. I thought request.body would give me the data, but i guess im wrong because i an getting this error. Any help or suggestions would be appreciated. Thanks
Error: Stripe: Argument "id" must be a string, but got: undefined (on
API request to POST /accounts/{id})
Your client code is trying to invoke a callable function, but your function is defined as an HTTP type function. They are different things. You can't invoke a regular HTTP function using the Functions SDK.
If you want to use the Functions SDK to invoke a function, it needs to be defined with onCall rather than onRequest, as shown in the documentation for callable functions. Or, if you don't want a callable on the backend, you will need to invoke the regular HTTP function with an HTTP client library.