SAM DynamoDB Application for Managing Orders
This is a sample application to demonstrate how to build an application on DynamoDB using the DynamoDBMapper ORM framework to map Order items in a DynamoDB table to a RESTful API for order management.
.
โโโ README.md <-- This instructions file
โโโ LICENSE.txt <-- Apache Software License 2.0
โโโ NOTICE.txt <-- Copyright notices
โโโ pom.xml <-- Java dependencies, Docker integration test orchestration
โโโ src
โ โโโ main
โ โ โโโ java
โ โ โโโ com.amazonaws.config <-- Classes to manage Dagger 2 dependency injection
โ โ โ โโโ OrderComponent.java <-- Contains inject methods for handler entrypoints
โ โ โ โโโ OrderModule.java <-- Provides dependencies like the DynamoDB client for injection
โ โ โโโ com.amazonaws.dao <-- Package for DAO objects
โ โ โ โโโ OrderDao.java <-- DAO Wrapper around the DynamoDBTableMapper for Orders
โ โ โโโ com.amazonaws.exception <-- Source code for custom exceptions
โ โ โโโ com.amazonaws.handler <-- Source code for lambda functions
โ โ โ โโโ CreateOrderHandler.java <-- Lambda function code for creating orders
โ โ โ โโโ CreateOrdersTableHandler.java <-- Lambda function code for creating the orders table
โ โ โ โโโ DeleteOrderHandler.java <-- Lambda function code for deleting orders
โ โ โ โโโ GetOrderHandler.java <-- Lambda function code for getting one order
โ โ โ โโโ GetOrdersHandler.java <-- Lambda function code for getting a page of orders
โ โ โ โโโ UpdateOrderHandler.java <-- Lambda function code for updating an order
โ โ โโโ com.amazonaws.model <-- Source code for model classes
โ โ โโโ request <-- Source code for request model classes
โ โ โ โโโ CreateOrderRequest.java <-- POJO shape for creating an order
โ โ โ โโโ GetOrDeleteOrderRequest.java <-- POJO shape for getting or deleting an order
โ โ โ โโโ GetOrdersRequest.java <-- POJO shape for getting a page of orders
โ โ โ โโโ UpdateOrderRequest.java <-- POJO shape for updating an order
โ โ โโโ response <-- Source code for response model classes
โ โ โ โโโ GatewayResponse.java <-- Generic POJO shape for the APIGateway integration
โ โ โ โโโ GetOrdersResponse.java <-- POJO shape for a page of orders
โ โ โโโ Order.java <-- POJO for Order resources
โ โโโ test <-- Unit and integration tests
โ โโโ java
โ โโโ com.amazonaws.config <-- Classes to manage Dagger 2 dependency injection
โ โโโ com.amazonaws.dao <-- Tests for OrderDao
โ โโโ com.amazonaws.handler <-- Unit and integration tests for handlers
โ โ โโโ CreateOrderHandlerIT.java <-- Integration tests for creating orders
โ โ โโโ CreateOrderHandlerTest.java <-- Unit tests for creating orders
โ โ โโโ DeleteOrderHandlerTest.java <-- Unit tests for deleting orders
โ โ โโโ GetOrderHandlerTest.java <-- Unit tests for getting one order
โ โ โโโ GetOrdersHandlerTest.java <-- Unit tests for getting a page of orders
โ โ โโโ UpdateOrderHandlerTest.java <-- Unit tests for updating an order
โ โโโ com.amazonaws.services.lambda.runtime <-- Unit and integration tests for handlers
โ โโโ TestContext.java <-- Context implementation for use in tests
โโโ template.yaml <-- Contains SAM API Gateway + Lambda definitions
Requirements
- AWS CLI already configured with at least PowerUser permission
- Java SE Development Kit 8 installed
- Docker installed
- Maven
- SAM CLI
- Python 3
Setup process
Installing dependencies
We use maven
to install our dependencies and package our application into a JAR file:
mvn package
Local development
Invoking function locally through local API Gateway
- Start DynamoDB Local in a Docker container.
docker run -p 8000:8000 amazon/dynamodb-local
- Create the DynamoDB table.
aws dynamodb create-table --table-name orders_table --attribute-definitions AttributeName=orderId,AttributeType=S --key-schema AttributeName=orderId,KeyType=HASH --billing-mode PAY_PER_REQUEST --endpoint-url http://localhost:8000
- Start the SAM local API.
- On a Mac:
sam local start-api --env-vars src/test/resources/test_environment_mac.json
- On Windows:
sam local start-api --env-vars src/test/resources/test_environment_windows.json
- On Linux:
sam local start-api --env-vars src/test/resources/test_environment_linux.json
If the previous command ran successfully you should now be able to hit the following local endpoint to
invoke the functions rooted at http://localhost:3000/orders
SAM CLI is used to emulate both Lambda and API Gateway locally and uses our template.yaml
to
understand how to bootstrap this environment (runtime, where the source code is, etc.) - The
following excerpt is what the CLI will read in order to initialize an API and its routes:
...
Events:
GetOrders:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /orders
Method: get
Packaging and deployment
AWS Lambda Java runtime accepts either a zip file or a standalone JAR file - We use the latter in
this example. SAM will use CodeUri
property to know where to look up for both application and
dependencies:
...
GetOrdersFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: target/aws-sam-java-rest-1.0.0.jar
Handler: com.amazonaws.handler.GetOrdersHandler::handleRequest
Firstly, we need a S3 bucket
where we can upload our Lambda functions packaged as ZIP before we
deploy anything - If you don't have a S3 bucket to store code artifacts then this is a good time to
create one:
export BUCKET_NAME=my_cool_new_bucket
aws s3 mb s3://$BUCKET_NAME
Next, run the following command to package our Lambda function to S3:
sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket $BUCKET_NAME
Next, the following command will create a Cloudformation Stack and deploy your SAM resources.
sam deploy \
--template-file packaged.yaml \
--stack-name sam-orderHandler \
--capabilities CAPABILITY_IAM
See Serverless Application Model (SAM) HOWTO Guide for more details in how to get started.
After deployment is complete you can run the following command to retrieve the API Gateway Endpoint URL:
aws cloudformation describe-stacks \
--stack-name sam-orderHandler \
--query 'Stacks[].Outputs'
Testing
Running unit tests
We use JUnit
for testing our code.
Unit tests in this sample package mock out the DynamoDBTableMapper class for Order objects.
Unit tests do not require connectivity to a DynamoDB endpoint. You can run unit tests with the
following command:
mvn test
Running integration tests
Integration tests in this sample package do not mock out the DynamoDBTableMapper and use a real AmazonDynamoDB client instance. Integration tests require connectivity to a DynamoDB endpoint, and as such the POM starts DynamoDB Local from the Dockerhub repository for integration tests.
mvn verify
Running end to end tests through the SAM CLI Local endpoint
Running the following end-to-end tests requires Python 3 and the requests
pip
package to be installed. For these tests to succeed,
pip3 install requests
python3 src/test/resources/api_tests.py 3
The number that follows the test script name is the number of orders to create in the test. For these tests to work, you must follow the steps for local development.
Appendix
AWS CLI commands
AWS CLI commands to package, deploy and describe outputs defined within the cloudformation stack:
sam package \
--template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket REPLACE_THIS_WITH_YOUR_S3_BUCKET_NAME
sam deploy \
--template-file packaged.yaml \
--stack-name sam-orderHandler \
--capabilities CAPABILITY_IAM \
--parameter-overrides MyParameterSample=MySampleValue
aws cloudformation describe-stacks \
--stack-name sam-orderHandler --query 'Stacks[].Outputs'
Bringing to the next level
Next, you can use the following resources to know more about beyond hello world samples and how others structure their Serverless applications: