Send a request with a body to AWS Lambda - python

I have uploaded an AWS Lambda function where the lambda_handler looks as follows:
import json
def lambda_handler(event, context):
print(event)
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!'),
'event': event
}
Problem 1: returning event
When I test it using the Lambda Management Console I can create a test event with parameters which also return the exact same format and all works fine:
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
However, when I use Postman, then I get something totally else, which returns to me:
{
"message": "Internal server error"
}
I suspect its because the event looks something more like:
{'resource': '/hello', 'path': '/hello', 'httpMethod': 'GET', 'headers': {'Accept': '*/*', ... etc
Problem 2: adding json parameters in body creates an error
When I try in Postman to add in the body > raw > JSON(application/JSON) the keys above, then I get the error:
ERROR: The request could not be satisfied
Questions
I have two questions:
How do I pass parameters in the body and be able to capture it in AWS lambda using the event or context?
How do I return the event properly?

Assuming you have set up your Lambda as proxy integration in AWS API Gateway. If you want to attach query string params and no body then your method type should be GET.
The event you would receive in your Lambda for request /GET your-path/?myKey1=value1&myKey2=value2 should something like:
{
"resource": "",
"path": "/your-path",
"httpMethod": "GET",
"headers": {
},
"queryStringParameters": {
"myKey1": "value1",
"myKey2": "value2"
},
"pathParameters": {
},
"body": "{}"
}
You can access query string params in queryStringParameters property.
If you send request using Postman and attach body then your Lambda integration type should be POST/PUT. Data that you add in Postman request body would be available in event["body"].
Last thing if you test Lambda directly in the console then event will be received as you put in the body. You will need to format your event according to the integration method type. If it is POST/PUT then:
{
"body": {
"someValue": {..}
}
}
If it is GET then:
{
"queryStringParameters": {
"myKey1": "value1",
"myKey2": "value2"
}
}

Figrued it out, after help from #Althar Khan.
Apparently the API Gateway of AWS Lambda only accepts certain properties:
...
return {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
In this response, there are four fields: statusCode, headers, body, and isBase64Encoded.
In this example, the function's response is in the format that the API Gateway expects. For additional information, see Output Format of a Lambda Function for Proxy Integration.

Related

How to retrieve a URL query string parameter from inside an AWS Lambda Python function?

How do you access URL querystring parameters from inside an AWS Lambda function served though an API Gateway?
I have both the API gateway + Lambda function setup so I can call it from a public URL. My Python function is simply:
def lambda_handler(event, context):
print('event:', event)
print('context:', context)
I've configured the API's GET "Method Request" handler to pass through the "abc" querystring parameter.
I've also configured the API's GET "Integration Request" handler to map "abc" from "method.request.querystring.abc".
However, when I access my URL, e.g. https://myapp.execute-api.us-east-1.amazonaws.com/prod/myfunc?abc=123, the only thing logged is:
event: {}
context: <bootstrap.LambdaContext object at 0x7fc7a6cb0850>
What am I doing wrong? Why isn't "abc" being passed through in the event dictionary?
Check Use Lambda Proxy integration in the Integration Request to have it pass all request details in the event.
I have a similar problem and I know how frustrating it is. Use this mapping template:
{
"method": "$context.httpMethod",
"body" : $input.json('$'),
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
#end
},
"queryStringParameters": {
#foreach($param in $input.params().querystring.keySet())
"$param": "$util.escapeJavaScript($input.params().querystring.get($param))" #if($foreach.hasNext),#end
#end
},
"pathParameters": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
}
}
Then you should find your event looks like this:
{
"method":"GET",
"body":{
},
"headers":{
},
"queryParams":{
"id":"459463732",
"command":"join_session"
},
"pathParams":{
}
}
The context is used for other information, such as IP addresses and timeout settings.

Error "name "true" not defined when sending POST request

I am trying to send a POST request that requires the literal true to be sent as part of the JSON payload. I know I can change it to True which Python likes but it fails with an API (SCIM) that I am testing.
How do I send the word true and have Python submit it as-is? This does work when I send via Postman. Using an example with httpbin below with the same error.
import requests
headers = {
"Accept": "Application/json",
"Content-Type": "Application/json"
}
payload = {
"emails": [{
"primary": true,
"value": "jack#justjack.com",
"type": "work"
}]
}
print (type (payload))
print (payload)
resp = requests.post ('http://httpbin.com/post',headers=headers,data=payload)
print(resp.content)
Error Message I get is
"primary": true,
NameError: name 'true' is not defined"
Use Python's regular True and convert your dict to a JSON string when you create your payload:
import json
payload = {
"emails": [{
"primary": True,
"value": "jack#justjack.com",
"type": "work"
}]
}
resp = requests.post('http://httpbin.com/post', headers=headers, data=json.dumps(payload))
# ^^^^^^^^^^
in python we are using True or False instead of true / false
you can read further here in PEP-285

Serverless Framework Python lambda return JSON directly

I'm trying to find out how can I return the response as a JSON directly using Serverless Framework.
This is a function on AWS with Lambda Proxy Integration. All default setting.
The goal is to from the python lambda function, the HTTP response client gets is directly a JSON object, instead of a string serialization of a JSON.
The python handler is as simple as this one
def handle(event, context):
log.info("Hello Wold")
log.info(json.dumps(event, indent=2))
return {
"statusCode": 200,
"body": {"foo": "bar"},
"headers": {
"Content-Type": "application/json"
}
}
The function looks like this:
functions:
report:
handler: handler.handle
events:
- http:
path: api/mypath
method: post
authorizer: aws_iam
With these configurations, the response BODY I get in Postman is:
{
"statusCode": 200,
"body": {
"foo": "bar"
},
"headers": {
"Content-Type": "application/json"
}
}
So this is strange, why I get everything as body? How do I configure it properly so that I only get the "real" body?
approach #1
Use json.dumps() to convert JSON to string.
import json
def handle(event, context):
log.info("Hello Wold")
log.info(json.dumps(event, indent=2))
return {
"statusCode": 200,
"body": json.dumps({"foo": "bar"}),
"headers": {
"Content-Type": "application/json"
}
}
approach #2
Use lambda integration and avoid json.dumps(). But this will transform your output as
{ foo = bar}
The body needs to be stringified when working with API Gateway
The pythonish way to do it is to pass the JSON body into the json.dumps function.
def handle(event, context):
log.info("Hello Wold")
log.info(json.dumps(event, indent=2))
return {
"statusCode": 200,
"body": json.dumps({"foo": "bar"}),
"headers": {
"Content-Type": "application/json"
}
}

Execute Lambda from AWS SSM Automation with Parameters

I currently have a lambda function that updates a DynamoDB table with a value passed as a parameter. I am able to run the following within the Lambda console with a test parameter set to "TEST":
import boto3
import json
def lambda_handler(event, context):
# TODO implement
update_ami(event)
def update_ami(ami_id):
#DO STUFF
I am attempting to call this from an SSM Automation built from the following JSON document:
{
"description":"Test Execute Lambda Function.",
"schemaVersion":"0.3",
"assumeRole":"MYARN",
"parameters":{},
"mainSteps":[
{
"name": "invokeMyLambdaFunction",
"action": "aws:invokeLambdaFunction",
"maxAttempts": 3,
"timeoutSeconds": 120,
"onFailure": "Abort",
"inputs": {
"FunctionName": "MyLambdaFunction",
"Payload": "TESTER"
}
}
]
}
Executing this automation results in the following error:
Automation Step Execution fails when it is invoking the lambda function. Get Exception from Invoke API of lambda Service. Exception Message from Invoke API: [Could not parse request body into json: Unrecognized token 'TESTER': was expecting ('true', 'false' or 'null')
I have also tried passing the Payload input as a JSON object instead of a string, and adjusted my lambda method accordingly:
JSON Automation:
...
"inputs": {
"FunctionName": "MyLambdaFunction",
"Payload": {
"ami_id": "AMI-TESTER"
}
}
...
Lambda Python:
def lambda_handler(event, context):
# TODO implement
update_ami(event['ami-id'])
This results in the following error coming from the Automation Document editor within the SSM console:
Input {ami_id=TESTER} is of type class java.util.LinkedHashMap, but expected type is String.
So in a nutshell... How do I pass a single string from an Automation document to a Lambda Function?
The error which is shown below looks to be issue with payload not passed as string:
Input {ami_id=TESTER} is of type class java.util.LinkedHashMap, but expected type is String
Please try to use escape character before double quotes.
"inputs": {
"FunctionName": "MyLambdaFunction",
"Payload": "{
\"ami_id\": \"AMI-TESTER\"
}"
}
AWS has provided proper syntax, if you see this Url
{
"name":"updateSsmParam",
"action":"aws:invokeLambdaFunction",
"timeoutSeconds":1200,
"maxAttempts":1,
"onFailure":"Abort",
"inputs":{
"FunctionName":"Automation-UpdateSsmParam",
"Payload":"{\"parameterName\":\"latestAmi\", \"parameterValue\":\"{{createImage.ImageId}}\"}"
}
}

What is wrong with my patch request to the Connectwise Rest API?

I've got a question about the Rest API for Connectwise. I've been doing get and post requests with no issue, but when I do a patch request I get a 400 response with 'field value is invalid' message regardless of what I try. I'm on 2016v1 and using the Rest API making calls from Python with the requests library.
The Rest API documentation says the following object is supposed to be passed in the body, but I haven't a clue what values are supposed to go with these keys:
{
op (string, optional),
path (string,optional),
value (string,optional)
}
I've tried dozens of calls including with the following bodies:
{'summary': 'updatedsummarytext'}
{'value': {'summary': 'updatedsummarytext'}}
{'op': {'summary': 'updatedsummarytext'}}
I have only gotten the following response so far:
<Response [400]>
{
"code": "InvalidObject",
"message": "operations object is invalid",
"errors": [
{
"code": "InvalidField",
"message": "The field value is invalid.",
"resource": "operations",
"field": "value"
}
]
}
Is their a specific value that connectwise is expecting for the op or value keys, or is there something I'm missing unique to Patch rest api calls?
The PATCH calls at a basic level use RFC6902.
Consider the following (simplified) Ticket document:
{
"summary": "Initial Ticket Summary",
"id": 1,
"company": {
"id": 5
},
"board": {
"id": 10
}
}
If you wish to update the summary field, your PATCH request JSON would look like this:
[
{"op": "replace", "path": "/summary", "value": "Updated Summary"}
]
Hope this helps.

Categories