Format body mapping templates in API Gateway - python

Need your help! I have the below Lambda function that will take inputs from the API Gateway (using RestAPI Post method) and pass the same as Payload to a second Lambda function.
def lambda_handler(event, context):
customerName = event['Name']
customerEmail = event['EmailAddress']
input = {"Name": customerName, "EmailAddress": customerEmail}
response = lambda_client.invoke(
FunctionName='arn_of_the_lambda_to_be_invoked',
InvocationType='Event',
Payload=json.dumps(input))
Below will be my input to the API Gateway in JSON format -
{
"Name": "TestUser",
"EmailAddress": "test#abc.com"
}
Have tried Lambda proxy integration and Generic Body Mapping templates (from here). In both occasions, API Gateway returns the below error -
Response Body:
{
"errorMessage": "'Name'",
"errorType": "KeyError",
"stackTrace": [
" File \"/var/task/lambda_function.py\", line 7, in lambda_handler\n customerName = event['Name']\n"
]
}
With the same JSON input when I directly invoke the Lambda from the Lambda console, it works. I know that API Gateway, along with the JSON body, pushes many other things. However, I'm unable to figure out.
How do I get this working?

In the lambda proxy integration, the event will be in the following format.
{
"resource": "Resource path",
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {String containing incoming request headers}
"multiValueHeaders": {List of strings containing incoming request headers}
"queryStringParameters": {query string parameters }
"multiValueQueryStringParameters": {List of query string parameters}
"pathParameters": {path parameters}
"stageVariables": {Applicable stage variables}
"requestContext": {Request context, including authorizer-returned key-value pairs}
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
the values you posted will be inside body as a json string. you need to parse the json.
import json
def lambda_handler(event, context):
fullBody = json.loads(event['body'])
print('fullBody: ', fullBody)
body = fullBody['body']
customerName = body['Name']
customerEmail = body['EmailAddress']
input = {"Name": customerName, "EmailAddress": customerEmail}
response = lambda_client.invoke(
FunctionName='arn_of_the_lambda_to_be_invoked',
InvocationType='Event',
Payload=json.dumps(input))
Input format of a Lambda function for proxy integration
Please remember that the lambda is expected to return the output in the following format for Lambda Proxy integration.
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... },
"body": "..."
}

Related

Returning a key value pair AWS Lambda function in python in a call centre connect instance

I am trying to create a connect instance with a lambda function. This lambda function needs to go into DynamDB and return the value associated with the primary key as a key value pair. The function is using python to program in.
What happens is that testing the function is passing, I am able to get a successful result through the test option with the function. However when I add the lambda function to my contact flow it is failing to even run the function successfully, and it is throwing the error "error". I think this is due to the result of it needing a key value repair.
Below is the code for the Lambda funcion:
import json
import boto3
client = boto3.client('dynamodb')
def lambda_handler(event, context):
data = client.get_item(
TableName='MessageFlat',
Key={'Message': {'S': "1"}
}
)
response = {
'statusCode': 200,
'body': json.dumps(data),
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
}
return response
This is the response given by the lambda function.
Response
{
"statusCode": 200,
"body": "{\"Item\": {\"Message\": {\"S\": \"1\"}, \"Hello\": {\"S\": \"Good Day. Welcome to Shelby's contact centre. \"}}, \"ResponseMetadata\": {\"RequestId\": \"S1CRNMSO7KM15TE5I70HJ2AL0FVV4KQNSO5AEMVJF66Q9ASUAAJG\", \"HTTPStatusCode\": 200, \"HTTPHeaders\": {\"server\": \"Server\", \"date\": \"Thu, 25 Aug 2022 22:54:10 GMT\", \"content-type\": \"application/x-amz-json-1.0\", \"content-length\": \"93\", \"connection\": \"keep-alive\", \"x-amzn-requestid\": \"S1CRNMSO7KM15TE5I70HJ2AL0FVV4KQNSO5AEMVJF66Q9ASUAAJG\", \"x-amz-crc32\": \"996959635\"}, \"RetryAttempts\": 0}}",
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
}
}
This is the needed output from the lambda function
{
"Message": "1",
"Hello": "Good Day. Welcome to Shelby's contact centre."
}
This is what my table is made up of.
If you simply want a subset of attributes of the DynamoDB item, then try this:
response = {
"Message": data["Item"]["Message"]["S"],
"Hello": data["Item"]["Hello"]["S"]
}
return response

Amazon Lex V2 Lambda python code to get intent name or any response back from lambda is not working

'''
Trying to get response back as name of the intent from lambda for Amazon Lex v2. It can be string or any response back in simple program.
I have referred the V2 Lex documentation but I can come-up with below code which shows error after several attempts. https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html
error : "Invalid Lambda Response: Received error response from Lambda: Unhandled"
'''
def lambda_handler(event, context):
entity = event["currentIntent"]["slots"]["Nm"].title()
intent = event["currentIntent"]["name"]
response = {
'sessionState': {
'dialogAction': {
'type': 'Close'
},
'state': 'Fulfilled'
},
'messages': [
'contentType': 'PlainText',
'content': "The intent you are in now is "+intent+"!"
],
}
return response
The 'messages' field is an array of objects, not an array of strings. It should be declared as follows:
'messages': [
{
'contentType': 'PlainText',
'content': "The intent you are in now is "+intent+"!"
}
]
Reference:
Amazon Lex - Lambda Response format
I faced the same issue. The solution that worked for me is like below
var response = {};
response.messages = [
message
];
response.sessionState = {
sessionAttributes:sessionAttributes,
intent : {
name : intentRequest.interpretations[0].intent.name,
state : 'Fulfilled'
},
dialogAction: {
type: "Close",
fulfillmentState: "Fulfilled"
}
};
Refer to lex v2 developer guide page 69 Response format
https://docs.aws.amazon.com/lexv2/latest/dg/lex2.0.pdf

How do I get an API response containing body, but no headers with Python & AWS Lambda?

I have created an API with AWS Lambda in Python. Unfortunately, the response contains the headers, and I would like it to only contain the body. The Lambda API looks like this:
import json
import boto3
def lambda_handler(event, context):
#Step1: Scan table
client = boto3.resource('dynamodb') #Access DynamoDB
table = client.Table("register-to-event") #Access table
response = table.scan()
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json",
},
"body": response["Items"]
}
The problem is that the API response contains headers and body when I call it. This is the response:
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": [
{
"your-email": "hannes.hannesen#googlemail.com",
"your-number": "004785454548",
"your-last-name": "Hannesen",
"your-first-name": "Hannes",
"ID": 3
},
{
"your-email": "stig.stigesen#googlemail.com",
"your-number": "+4754875456",
"your-last-name": "Stigesen",
"your-first-name": "Stig",
"ID": 0
}
]
}
The goal is to call the API and return only the body which is json like this:
[
{
"your-email": "hannes.hannesen#googlemail.com",
"your-number": "004785454548",
"your-last-name": "Hannesen",
"your-first-name": "Hannes",
"ID": 3
},
{
"your-email": "stig.stigesen#googlemail.com",
"your-number": "+4754875456",
"your-last-name": "Stigesen",
"your-first-name": "Stig",
"ID": 0
}
]
The Solution was to configure the API in AWS correctly by ticking the box next to "Use Lambda Proxy integration":
Use Lambda Proxy integration
My next problem ended up being "serializing decimals" as a result of DynamoDB making the keys of the table a type (Decimal) which does not exist in Python.
The solution to this can be found here: https://learn-to-code.workshop.aws/persisting_data/dynamodb/step-3.html

Getting query string parameters from API Gateway

I am using Amazon API Gateway to invoke a lambda function. I am testing a get request with the following as my query string earlyDate="12-01-21"&laterDate="12-03-21".
I currently have my lambda function returning the event that gets passed:
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps(event)
}
This works as expected. When I test my API gateway I get a response which includes
"queryStringParameters": {
"earlyDate": "12-01-21",
"laterDate": "12-03-21"
},
"multiValueQueryStringParameters": {
"earlyDate": [
"12-01-21"
],
"laterDate": [
"12-03-21"
]
},
This indicates I should be able to access these query parameters at event.queryStringParameters. However when I change my lambda function to return those:
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps(event.queryStringParameters)
}
The result is a 502 error.
How do I access the query string parameters passed in from my API Gateway?
I think, event is a dict and we can access its query params as event['queryStringParameters']
We can confirm that by
for key, value in event.items():
print(key, value)
Lambda could return this:
return {
'statusCode': 200,
'body': json.dumps(event['queryStringParameters'])
}

Decoding Django POST request body

I'm building an mapping app using cordova and making a post request sending the following JSON (feature)
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-6.6865857,
53.2906136
]
},
"properties": {
"amenity": "pub",
"name": "The Parade Ring"
}
}
This is the JQuery code sending the request
function savePub(feature){
$.ajax({
type: "POST",
headers: {"csrfmiddlewaretoken": csrftoken},
url: HOST + URLS["savePub"],
data: {
pub_feature: JSON.stringify(feature)
},
contentType:"application/json; charset=utf-8"
}).done(function (data, status, xhr) {
console.log(data + " " + status);
pubDialogAlert("Pub saved",feature);
}).fail(function (xhr, status, error) {
showOkAlert(error);
console.log(status + " " + error);
console.log(xhr);
}).always(function () {
$.mobile.navigate("#map-page");
});
}
When the request is received in the Django backend I am not sure why when I print the request body it looks like this,
b'pub_feature=%22%7B%5C%22type%5C%22%3A%5C%22Feature%5C%22%2C%5C%22geometry%5C%22%3A%7B%5C%22type%5C%22%3A%5C%22Point%5C%22%2C%5C%22coordinates%5C%22%3A%5B-6.6865857%2C53.2906136%5D%7D%2C%5C%22properties%5C%22%3A%7B%5C%22amenity%5C%22%3A%5C%22pub%5C%22%2C%5C%22name%5C%22%3A%5C%22The+Parade+Ring%5C%22%7D%7D%22'
and when I try to decode it and then use json.loads() it throws this error
#api_view(['POST'])
def save_pub(request):
if request.method == "POST":
data = request.body.decode('utf-8')
received_json_data = json.loads(data)
return Response(str(received_json_data) + " written to db", status=status.HTTP_200_OK)
JSONDecodeError at /savepub/
Expecting value: line 1 column 1 (char 0)
I am assuming because once it decodes the binary string it can't be converted to valid JSON because of those characters %22 etc, but I don't know what the solution is.
Any help would be appreciated.
Thanks
You're mixing up two things here, form-encoded and JSON format. What you have is a form-encoded post with one key, pub_feature, whose value is a JSON object.
Instead you should post the JSON directly:
data: JSON.stringify(feature),
and then you should be able to load it as you do already - although note that really you should let DRF deal with that for you

Categories

Resources