AWS Lambda Lifesavers: print(event)
Developing AWS Lambda functions that integrate with other AWS services? For example, consuming SNS Topics forwarded from CloudWatch Alarms, reacting to GuardDuty Findings or AWS Config rule violations. All of these AWS services emit events in the format of a JSON data structure.
One of the biggest source of errors and unexpected lengthy debug sessions for such type of Lambdas comes from not processing the JSON in the format the developer expected. To avoid this, I start such a Lambda with just two lines:
--- myapp.py
def lambda_handler(event, context):
print(event)
Then I will trigger whatever AWS service I'm using to send an event to invoke the Lambda and capture an example of a delivered JSON file. I'll usually also consult the AWS documentation for the delivered JSON format, although for many services this documentation may be quite thin and hard to find.
With an example JSON event captured, I'll take that event and save it within the test suite alongside the Lambda.
Having example JSON events saved somewhere near your Lambda code will save you or anyone who follows doing maintenance a huge amount of effort!
My preferred way to test consuming the example JSON in Python is to write a simple unit test. In a second file, I'd write:
Recommended by LinkedIn
--- test_myapp.py
import myapp
import unittest
import json
example_event = json.loads("""
<aws-json-example-here>
""")
class TestEvent(unittest.TestCase):
def test_get_event(self):
result = myapp.parse_event(example_event)
assertTrue(result)
Finally, I'd begin to write the Lambda, starting with a skeleton that looks like this:
--- myapp.py
def lambda_handler(event, context):
os.environ.get('PARSE_ALL', true)
main(event, PARSE_ALL)
def parse_event(event, parse_all):
# put some actual parsing code here ...
if parse_all:
return event['Message'][0]['Field1']
return {'msg': 'Hey now!'}
def main(event, parse_all):
data = parse_event(event, parse_all)
This is pretty straight-forward. Simply ensure the JSON parsing and consumption is in it's own function or method so that it can be cleanly unit tested.
However, you'll also notice that I've made an extra main() function. This is with the intent that if I later write integration testing (perhaps using mocks with the amazing moto library), then I've already separated the Lambda invocation logic from the main control function. Where "Lambda invocation logic" is 99% of the time just consuming Lambda environment variables or setting defaults if they're not set.
Happy Lambda testing!