Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Python example for api-sqs-lambda #295

Merged
merged 9 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions python/api-sqs-lambda/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

# API Gateway + SQS + Lambda
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a section at the end of this file, letting people know how they can interact with the APIGateway after the stack has been deployed and how to check the message was received by the Lambda function and view the contents of the message via the function's logs?

You could provide a sequence of AWS CLI commends with 1 line commentary on what each command is doing, similar to the steps you've used to describe the steps to activate virtualenv and cdk synth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added testing instructions in the README file. Hope this helps!


Creates an API Gateway API with a POST Method, a SQS queue, and a Lambda function. Requests to the API are enqueued into the SQS queue, which triggers the Lambda function.

![Architecture](architecture.png)

The `cdk.json` file tells the CDK Toolkit how to execute your app.

This project is set up like a standard Python project. The initialization process also creates a virtualenv within this
project, stored under the `.env` directory. To create the virtualenv it assumes that there is a `python3` (or `python`
for Windows) executable in your path with access to the `venv` package. If for any reason the automatic creation of the
virtualenv fails, you can create the virtualenv manually.

To manually create a virtualenv on MacOS and Linux:

```
$ python3 -m venv .env
```

After the init process completes and the virtualenv is created, you can use the following
step to activate your virtualenv.

```
$ source .env/bin/activate
```

If you are a Windows platform, you would activate the virtualenv like this:

```
% .env\Scripts\activate.bat
```

Once the virtualenv is activated, you can install the required dependencies.

```
$ pip install -r requirements.txt
```

At this point you can now synthesize the CloudFormation template for this code.

```
$ cdk synth
```

nija-at marked this conversation as resolved.
Show resolved Hide resolved
Upon successful deployment, you should see an API Gateway REST API in your account. It can be tested from the console or the CLI:

```
$ aws apigateway test-invoke-method --rest-api-id <API ID> --resource-id <RESOURCE ID> --http-method POST --body {"key":"value"}
```

This request should complete with a 200 OK. The Lambda function should print the API Gateway request body in its CloudWatch logs. (https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html)

```
2020-05-27T11:50:44.755-05:00 Received Message Body from API GW: {key:value}
```

This message will also be visible in the SQS Queue metrics in CloudWatch. (https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-access-metrics.html)
Empty file.
78 changes: 78 additions & 0 deletions python/api-sqs-lambda/api_sqs_lambda/api_sqs_lambda_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from aws_cdk import (
aws_sqs as sqs,
aws_iam as iam,
aws_apigateway as apigw,
aws_lambda as _lambda,
aws_lambda_event_sources as lambda_event_source,
core
)

class ApiSqsLambdaStack(core.Stack):

def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

#Create the SQS queue
queue = sqs.Queue(self, "SQSQueue")

#Create the API GW service role with permissions to call SQS
rest_api_role = iam.Role(
self,
"RestAPIRole",
assumed_by=iam.ServicePrincipal("apigateway.amazonaws.com"),
managed_policies=[iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSQSFullAccess")]
)

#Create an API GW Rest API
base_api = apigw.RestApi(self, 'ApiGW',rest_api_name='TestAPI')

#Create a resource named "example" on the base API
api_resource = base_api.root.add_resource('example')


#Create API Integration Response object: https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_apigateway/IntegrationResponse.html
integration_response = apigw.IntegrationResponse(
status_code="200",
response_templates={"application/json": ""},

)

#Create API Integration Options object: https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_apigateway/IntegrationOptions.html
api_integration_options = apigw.IntegrationOptions(
credentials_role=rest_api_role,
integration_responses=[integration_response],
request_templates={"application/json": "Action=SendMessage&MessageBody=$input.body"},
passthrough_behavior=apigw.PassthroughBehavior.NEVER,
request_parameters={"integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"},
)

#Create AWS Integration Object for SQS: https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_apigateway/AwsIntegration.html
api_resource_sqs_integration = apigw.AwsIntegration(
service="sqs",
integration_http_method="POST",
path="{}/{}".format(core.Aws.ACCOUNT_ID, queue.queue_name),
options=api_integration_options
)

#Create a Method Response Object: https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_apigateway/MethodResponse.html
method_response = apigw.MethodResponse(status_code="200")

#Add the API GW Integration to the "example" API GW Resource
api_resource.add_method(
"POST",
api_resource_sqs_integration,
method_responses=[method_response]
)

#Creating Lambda function that will be triggered by the SQS Queue
sqs_lambda = _lambda.Function(self,'SQSTriggerLambda',
handler='lambda-handler.handler',
runtime=_lambda.Runtime.PYTHON_3_7,
code=_lambda.Code.asset('lambda'),
)

#Create an SQS event source for Lambda
sqs_event_source = lambda_event_source.SqsEventSource(queue)

#Add SQS event source to the Lambda function
sqs_lambda.add_event_source(sqs_event_source)
11 changes: 11 additions & 0 deletions python/api-sqs-lambda/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env python3

from aws_cdk import core

from api_sqs_lambda.api_sqs_lambda_stack import ApiSqsLambdaStack


app = core.App()
ApiSqsLambdaStack(app, "ApiSqsLambdaStack")

app.synth()
Binary file added python/api-sqs-lambda/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions python/api-sqs-lambda/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"app": "python3 app.py",
"context": {
"@aws-cdk/core:enableStackNameDuplicates": "true",
"aws-cdk:enableDiffNoFail": "true"
}
}
10 changes: 10 additions & 0 deletions python/api-sqs-lambda/lambda/lambda-handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import json
import boto3

def handler(event, context):
response = "Received Message Body from API GW: " + event['Records'][0]['body']
print(response)
return {
'statusCode': 200,
'body': response
}
7 changes: 7 additions & 0 deletions python/api-sqs-lambda/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
aws-cdk.core

aws-cdk.aws_lambda
aws-cdk.aws_lambda_event_sources
aws-cdk.aws_sqs
aws-cdk.aws_iam
aws-cdk.aws_apigateway
45 changes: 45 additions & 0 deletions python/api-sqs-lambda/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import setuptools


with open("README.md") as fp:
long_description = fp.read()


setuptools.setup(
name="api_sqs_lambda",
version="0.0.1",

description="A CDK Python app to create the API GW + SQS + Lambda integration",
long_description=long_description,
long_description_content_type="text/markdown",

author="Akshit Khanna",

package_dir={"": "api_sqs_lambda"},
packages=setuptools.find_packages(where="api_sqs_lambda"),

install_requires=[
"aws-cdk.core==1.41.0",
],

python_requires=">=3.6",

classifiers=[
"Development Status :: 4 - Beta",

"Intended Audience :: Developers",

"License :: OSI Approved :: Apache Software License",

"Programming Language :: JavaScript",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",

"Topic :: Software Development :: Code Generators",
"Topic :: Utilities",

"Typing :: Typed",
],
)