Python lambda function returns KeyError - python

I'm trying to create simple Lambda function using Python 3.6.
The function should get a userId (my primary key in DynamoDB) in the request query string params and returns 200 if item exist in DB, here is my lambda function
import boto3
import os
from boto3.dynamodb.conditions import Key, Attr
def lambda_handler(event, context):
userId = event["userId"]
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['Customers'])
items = table.query(
KeyConditionExpression=Key('userId').eq(userId)
)
return items["Items"]
When i am doing tests in Lambda interface it works and return the correct user however, when trying from Postman or using API Gateway it returns the following error
{
"errorMessage": "'userId'",
"errorType": "KeyError",
"stackTrace": [
[
"/var/task/index.py",
7,
"lambda_handler",
"userId = event["userId"]"
]
]
}
What am i missing here ?
Struggling to understand "event" , documentation states its a python
dictionary but how can i print the result of it and actually debug the lambda
when called from Postman or API Gateway?

You are using event["userId"], this means that sending the request payload for example
GET API : api/users/
Request Body payload:
{
"userId":"1234"
}
then above code works, Suppose you want to send userId as path parameter
GET API :api/user/{userId}
then you can access in lambda function
userId = (event['pathparameters']['userId'])
better add the print statement
print(event) and check the logs in cloudwatch logs

This solved it for me on post requests
import json
def lambda_handler(event, context):
data = json.loads(event["body"])
email = data['email']
in case you are using the serverless framework you can also add the following code under your http event. but i dont think it is that necessary.
request:
parameters:
application/json: '{"email":"$input.params(''email'')"}'

Make sure you hadn't selected "Lambda Proxy" while creating the HTTP method. Proxy will not convert/modify the request and hence "event" will be null

In my case my Python Lambda required a key called exclude. To resolve the issue of getting this response when calling via API Gateway, I needed to update the integration request with a mapping template:

Related

How to add enum resolver to Flask GraphQL server using Ariadne

I want to pass a custom resolver for enum type into my GraphQL server created using Flask and Ariadne.
In my schema.graphql file, I have defined an enum:
enum UtilizationScore {
ELEVATED
HIGH
NORMAL
}
And I want to assign values to each enum field like this:
utilization_score = EnumType(
"UtilizationScore", {
'ELEVATED': 20,
'HIGH': 30,
'NORMAL': 10
}, )
This is my GraphQL API:
#app.route("/graphql", methods=["POST"])
def graphql_server():
data = request.get_json()
print('GraphQL request: ', data)
success, result = graphql_sync(
schema,
data,
context_value=request,
debug=app.debug
)
print('Result from resolver: ', success, result)
status_code = 200 if success else 400
return jsonify(result), status_code
Now, I need to pass this utilization_score to my GraphQL server, which I have no idea how to do.
In the Ariadne documentation, I found this statement - (In my case, I have utilization_score instance instead of post_weight instance)
Include the post_weight instance in list of types passed to your GraphQL server, and it will automatically translate Enums between their GraphQL and Python values.
But I still couldn't figure out where to pass this enum resolver instance.

How can I troubleshoot my AWS Lambda function?

I created a lambda function in AWS and apparently it is throwing an error. Here it is:
import json
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
import boto3
#Create a SSM Client to access parameter store
ssm = boto3.client('ssm')
def lambda_handler(event, context):
# TODO implement
#return {
# 'statusCode': 200,
# 'body': json.dumps('Hello from Lambda!')
#}
slack_message = {
'text' = f'Hello World'
}
#retrieve webhook url from parameter store
webhook_url = ssm.get_parameter(Name='slackwebhookurl', WithDecryption=True)
#make request to the API
req = Request(webhook_url['Parameter']['Value'],
json.dumps(slack_message).encode('utf-8'))
try:
response = urlopen(req)
response.read()
print("Messge posted to Slack")
except HTTPError as e:
print(f'Request failed: {e.code} {e.reason})
except URLError as e:
print(f'Server Connection failed: {e.reason})
It's triggered by an AWS SNS notification. It's supposed to grab a webhook url for a slack channel and then send the notification to Slack.
Can anyone see what the problem is?
If it's not obvious, can someone direct me to a tutorial on how to test AWS Lambda functions?
Thanks.
AWS Lambda has an in-built Test functionality. You can click the Test button and configure an input in case the function uses values from event.
Logs will be displayed within the Lambda console.
Also, ensure that the IAM Role associated with the AWS Lambda function has the AWSLambdaBasicExecutionRole permission policy so that it can write to CloudWatch Logs. Then, you can go to the Monitoring tab of the function and click View logs in CloudWatch to see past logs.
You can add print() statements in your code, which will appear in the Logs.

Python http delete requests with URL path variable

I have an Http endpoint exposed as http://localhost:8080/test/api/v1/qc/{id} for delete, while making this API delete call I have to replace with the proper id
I tried below way using the requests module of python
param = {
"id" : 1
}
requests.delete(url = http://localhost:8080/test/api/v1/qc/{id}, params=param)
This API call is breaking with the error
ValueError: No JSON object could be decoded.
How can I do this?
Your code can't run as-is. You need to quote your url string:
url = "http://localhost:8080/test/api/v1/qc/{id}"
Reading the docs for requests, the params only sends the dictionary param as the query string, so it'll only tack on ?id=1 to the end of the URL.
What you want is the {id} to get the value from the dictionary. You can look at this answer for various ways: How do I format a string using a dictionary in python-3.x?
You want something like
requests.delete(url = "http://localhost:8080/test/api/v1/qc/{id}".format(**param))

Got QueryString error when executing query on athena with boto client

I'm trying to execute my query using Athena boto client.
self.athena_client = boto3.client('athena')
response = self.athena_client.start_query_execution(
QueryString=sql.format(**query_params) if query_params else sql,
ResultConfiguration={
'OutputLocation': '...'
}
)
But I'm getting the error bellow:
Invalid length for parameter QueryString, value: 0, valid range: 1-inf
I couldn't figure out what is the root cause.
You should certify you are correctly sending the QueryString param to boto method start_query_execution.
It's a required parameter and it'll throw this error when you, for example, request send QueryString with an empty string.
Check more here at boto3 docs

How to set Zappa to invoke lambda function directly?

Here's my backend structure:
Here's my app.py:
from flask import Flask
app = Flask(__name__)
#app.route('/', methods=['GET'])
def test_backend():
return "This is the test function for backend without lambda"
if __name__ == '__main__':
app.run(debug=True)
and lambda_handler in event_lambda.py:
def lambda_handler(event=None, context=None):
""" This lambda triggers other supporting functions """
return "This lambda handler triggers other functions "
I've tried to invoke lambda function through the following event in zappa_settings.json
"events": [{
"function": "backend.event_lambda.lambda_handler",
"expression": "cron(0 9 1 * ? *)"
}],
But it only returns "This is the test function for backend without lambda" from the app.py. The lambda function is invoked only when I invoke it manually using the command:
zappa invoke backend.event_lambda.lambda_handler
How can I set zappa to invoke the lambda function directly?
Im not sure if this is exactly what you are looking for, but this response https://stackoverflow.com/a/62119981 was a godsend for me while I was trying to invoke a function that was within zipped API deployed via Zappa on AWS Lambda.
Relevant fragment in source code:
https://github.com/zappa/Zappa/blob/fff5ed8ad2ee071896a94133909adf220a8e47d9/zappa/handler.py#L380
TL;DR: use command keyword in payload of what is send to lambda_handler to invoke any function in your API.
so, if you would like to invoke your lambda_handler function which is part of an zipped API deployed on Lambda, you can do it via boto3:
from json import dumps
import boto3
lambda_client = boto3.Session().client(
service_name='lambda',
region_name='eu-central-1' # or other region
)
response = lambda_client.invoke(
FunctionName=<lambda_arn>,
Payload=dumps(
{
"command": "xxx_backend.event_lambda.lambda_handler", # xxx is the blacked out fragment of the name
}
)
)
And in response, apart from some Metadata, you should receive desired ("This lambda handler triggers other functions ") output.
It's also possible to pass some data into handler, BUT im not sure if there is any recommended keyword, so you can use any (beside those which are reserved!). Im using 'data'.
So, we will change your lambda_handler function a little bit:
def lambda_handler(event=None, context=None):
""" This lambda triggers other supporting functions """
return f"This lambda handler received this payload: {event["data"]}"
The invokation has to change too:
from json import dumps
import boto3
lambda_client = boto3.Session().client(
service_name='lambda',
region_name='eu-central-1' # or other region
)
response = lambda_client.invoke(
FunctionName=<lambda_arn>,
Payload=dumps(
{
"command": "xxx_backend.event_lambda.lambda_handler", # xxx is the blacked out fragment of the name
"data": "Hello from Lambda!"
}
)
)
And the response from the invokation should look like this now:
This lambda handler received this payload: Hello from Lambda!"
Hope this helps.
Try:
zappa schedule <environment>

Categories