How to set Zappa to invoke lambda function directly? - python

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>

Related

Acknowledge request has been received to the user

I want to send a acknowledgement to the user that request has been received and FUNCTION_NAME has been called. The FUNCTION_NAME internally calls other functions and takes about 10-15 mins to complete. So the user is acknowledged that his request is captured for processing. Any hints/leads towards handing this approaching
import os
import json
import boto3
import json as js
lambda_client = boto3.client('lambda')
def lambda_handler(event, context):
res=lambda_client.invoke(FunctionName='FUNCTION_NAME',InvocationType='RequestResponse',Payload=js.dumps(event))
res=res['Payload'].read().decode()
data= js.loads(res)['body']
status=js.loads(res)['statusCode']
return {
'isBase64Encoded':"false",
'statusCode': status,
'body': data
}
As mentioned in the boto3 documentation, you can invoke the other lambda synchronously or asynchronously.
To invoke the lambda asynchronously, change the InvocationType to Event.
See the documentation for reference.
Please do not use invoke_async, this is deprecated.

pass a parameter from http request as a column name does not work in python azure function

I'm writing a http-triggered python azure function for my project. Basically, the function call the database, processed data and send response as json data.
code example as below. name is a string from http request, which is passed as a column name.
The function below does not work when I tested in my vscode with REST api client extension.
POST http://localhost:7071/api/functionname
Content-Type: application/json
{
"name": "mycolumnName"
}
When I comment out the line df=df[name], then this azure function works fine. So pretty sure the problem is from this line.
Dose anyone know why? How can I pass parameter from http request as column name of a dataframe? I feel like I'm missing something....
Below is the simplified azure function.
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
# call database and generate a dataframe: df
......
......
# the issue is this line
df = df[name]
return func.HttpResponse(df.to_json()

Call function in another AWS lambda using existing call

Please refer the code snippet below:
import awsgi
import json
from flask import (
Flask,
jsonify,
request
)
app = Flask(__name__)
#app.route('/')
def index():
return jsonify(status=200, message='OK')
#app.route('/tester')
def tst():
rule = request.url_rule
if 'tester' in rule.rule:
return {'status':200, 'message':'test'}
def lambda_handler(event, context):
test = (awsgi.response(app, event, context))
for key, value in test.items():
if key == 'message':
call = value
return {
'body': json.dumps(test)
}
Now in call variable we have value 'test'.
This 'test' is also the name of a method in another lambda that I want to call.
can someone please help me with this
Thanking You
Each AWS Lambda function has one entry point via the function defined as the Handler.
When the AWS Lambda function is invoked, the Handler function is called. It is not possible to 'call' another function when invoking the Lambda function.
However, you could add some logic to the Handler function that examines the incoming event and checks for a test situation. The Handler function could then call the test() function. For example, add an entry to the event that says "Test": "true", then have the Handler function check for this entry and, if present, call test().
I think you you simply would like to call another lambda.
If thats what you are looking for, here is how you can do it.
import json
import boto3
from flask import (
Flask,
jsonify,
request
)
import awsgi
client = boto3.client('lambda')
def lambda_handler(event, context):
test = 'other-function-name'
response = client.invoke(
FunctionName=test,
InvocationType='RequestResponse',
Payload=json.dumps({})
)
return {
...
'body': json.dumps(response)
....
}
If you don't like to wait for the other lambda to finish, you can use InvocationType='RequestResponse'
hope this helps.

Python lambda function returns KeyError

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:

Using boto to invoke lambda functions how do I do so asynchronously?

SO I'm using boto to invoke my lambda functions and test my backend. I want to invoke them asynchronously. I have noted that "invoke_async" is deprecated and should not be used. Instead you should use "invoke" with an InvocationType of "Event" to do the function asynchronously.
I can't seem to figure out how to get the responses from the functions when they return though. I have tried the following:
payload3=b"""{
"latitude": 39.5732160891,
"longitude": -119.672918997,
"radius": 100
}"""
client = boto3.client('lambda')
for x in range (0, 5):
response = client.invoke(
FunctionName="loadSpotsAroundPoint",
InvocationType='Event',
Payload=payload3
)
time.sleep(15)
print(json.loads(response['Payload'].read()))
print("\n")
Even though I tell the code to sleep for 15 seconds, the response variable is still empty when I try and print it. If I change the invokation InvokationType to "RequestResponse" it all works fine and response variable prints, but this is synchronous. Am I missing something easy? How do i execute some code, for example print out the result, when the async invokation returns??
Thanks.
There is a difference between an 'async AWS lambda invocation' and 'async python code'. When you set the InvocationType to 'Event', by definition, it does not ever send back a response.
In your example, invoke() immediately returns None, and does not implicitly start up anything in the background to change that value at a later time (thank goodness!). So, when you look at the value of response 15 seconds later, it's still None.
It seems what you really want is the RequestResponse invocation type, with asynchronous Python code. You have a bunch of options to choose from, but my favorite is concurrent.futures. Another is threading.
Here's an example using concurrent.futures:
(If you're using Python2 you'll need to pip install futures)
from concurrent.futures import ThreadPoolExecutor
import json
payload = {...}
with ThreadPoolExecutor(max_workers=5) as executor:
futs = []
for x in xrange(0, 5):
futs.append(
executor.submit(client.invoke,
FunctionName = "loadSpotsAroundPoint",
InvocationType = "RequestResponse",
Payload = bytes(json.dumps(payload))
)
)
results = [ fut.result() for fut in futs ]
print results
Another pattern you might want to look into is to use the Event invocation type, and have your Lambda function push messages to SNS, which are then consumed by another Lambda function. You can check out a tutorial for SNS-triggered lambda functions here.
An asynchronously executed AWS Lambda function doesn't return the result of execution. If an asynchronous invocation request is successful (i.e. there were no errors due to permissions, etc), AWS Lambda immediately returns the HTTP status code 202 ACCEPTED and bears no further responsibility for communicating any information about the outcome of this asynchronous invocation.
From the documentation of AWS Lambda Invoke action:
Response Syntax
HTTP/1.1 StatusCode
X-Amz-Function-Error: FunctionError
X-Amz-Log-Result: LogResult
Payload
Response Elements
If the action is successful, the service sends back the following HTTP
response.
StatusCode
The HTTP status code will be in the 200 range for successful request.
For the RequestResponse invocation type this status code will be 200.
For the Event invocation type this status code will be 202. For the DryRun invocation type the status code will be 204.
[...]
The response returns the following as the HTTP body.
Payload
It is the JSON representation of the object returned by the Lambda
function. This is present only if the invocation type is
RequestResponse.
The following are a python function that accepts lambda-function-Name to invoke and payload to send to that function.
It invokes the lambda function by boto3 client.
import boto3, json, typing
def invokeLambdaFunction(*, functionName:str=None, payload:typing.Mapping[str, str]=None):
if functionName == None:
raise Exception('ERROR: functionName parameter cannot be NULL')
payloadStr = json.dumps(payload)
payloadBytesArr = bytes(payloadStr, encoding='utf8')
client = boto3.client('lambda')
return client.invoke(
FunctionName=functionName,
InvocationType="RequestResponse",
Payload=payloadBytesArr
)
And usage:
if __name__ == '__main__':
payloadObj = {"something" : "1111111-222222-333333-bba8-1111111"}
response = invokeLambdaFunction(functionName='myLambdaFuncName', payload=payloadObj)
print(f'response:{response}')

Categories