Search code examples
pythonapiaws-lambdaaws-iot-analytics

Lambda function trigerred in IoT Analytics which sends POST requests to an external API


I am trying to set an AWS Lambda function in Python, which is triggered during an IOT Analytics treatment and sends two successive POST requests to an external API (if a condition is met). I am unable to import the "request" package directly, as it is too large for inline editing, so I have uploaded my Python script as well as the package into the zip file, then uploading it to AWS.

When running the lambda, I get errors on the requests package files, which I don't really understand. I am also unsure about how to return the API response, as I get a serialization error on my response. Here is my lambda code:

import json
import os
import time
import requests

def lambda_handler(event,context):

headers = {
        "authorization": "Basic XXXXXXXXX",
        "content_type": "application/json"
        } #API request header

url = "https://example.com/path" #API request URL

msgs = json.loads(json.dumps(event))
for msg in msgs:
    deviceID = msg["deviceID"]
    data = msg["data"]

    if (condition over received message):
        body1 = {
            "paramAA": paramAA,
            "paramB": [{
                    "paramBA": paramBA1,
                    "paramBB": paramBB
                    }]
            }
        response_1 = requests.post(url,headers=headers,data=body1) #First API request

        time.sleep(600)

        body2 = {
            "paramAA": paramAA,
            "paramB": [{
                "paramBA": paramBA2,
                "paramBB": paramBB,
                }]
            }
        response_2 = requests.post(url,headers=headers,data=body2) #Second API request
    else:
        pass
else:
    pass

return {
    'statusCode': 200,
    'url' : url,
    'response_1.code' : response_1.status_code,
    'response_1_msg' : response_1.text,
    'response_2.code' : response_2.status_code,
    'response_2_msg' : response_2.text
    }

Do you have any idea how to fix these errors?

If I change the return with "reponse_1 : json.dumps(response1)", I get these errors (whether it is with the zip package or the SDK):

{
"errorMessage": "Object of type Response is not JSON serializable",
"errorType": "TypeError",
"stackTrace": [
  "  File \"/var/task/lambda_function.py\", line 56, in lambda_handler\n    'response_1' : json.dumps(response_1),\n",
  "  File \"/var/lang/lib/python3.8/json/__init__.py\", line 231, in dumps\n    return _default_encoder.encode(obj)\n",
  "  File \"/var/lang/lib/python3.8/json/encoder.py\", line 199, in encode\n    chunks = self.iterencode(o, _one_shot=True)\n",
  "  File \"/var/lang/lib/python3.8/json/encoder.py\", line 257, in iterencode\n    return _iterencode(o, 0)\n",
  "  File \"/var/lang/lib/python3.8/json/encoder.py\", line 179, in default\n    raise TypeError(f'Object of type {o.__class__.__name__} '\n"

Solution

  • It appears that json library can convert to string only standard types, but I had to use pickle for more complex objects. I have modified my code accordingly, and ended up with a utf-8 encoding error :

      "errorMessage": "Unable to marshal response: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte"
    

    Here is the script that I have now:

    import pickle
    import os
    import time
    import requests
    
    def lambda_handler(event,context):
    
    headers = {
            "authorization": "Basic XXXXXXXXX",
            "content_type": "application/json"
            } #API request header
    
    url = "https://example.com/path" #API request URL
    
    msgs = pickle.loads(pickle.dumps(event))
    for msg in msgs:
        deviceID = msg["deviceID"]
        data = msg["data"]
    
        if (condition over received message):
            body1 = {
                "paramAA": paramAA,
                "paramB": [{
                        "paramBA": paramBA1,
                        "paramBB": paramBB
                        }]
                }
            response_1 = requests.post(url,headers=headers,data=pickle.dumps(body1)) #First API request
            exec_verif = "request 1 sent"
    
            time.sleep(600)
    
            body2 = {
                "paramAA": paramAA,
                "paramB": [{
                    "paramBA": paramBA2,
                    "paramBB": paramBB,
                    }]
                }
            response_2 = requests.post(url,headers=headers,data=pickle.dumps(body2)) #Second API request
            exec_verif = "request 2 sent"
        else:
            response_1 = "fail1"
            response_2 = "fail1"
    else:
        response_1 = "fail2"
        response_2 = "fail2"
    
    return {
        'statusCode': 200,
        'exec_verif' : exec_verif,
        'response_1' : pickle.dumps(response_1),
        'response_2' : pickle.dumps(response_2)
        }
    

    Something strange to me is that, if I don't include response_1 and response_2 in the return, the function is executed and sends back a 200 status, with 'exec_verif' being "request 2 sent", while there is no actual call to the external API. There is no trace of any call, even unsuccessfull, in the API logs.