Skip to content

A sample REST application built on SAM and DynamoDB that demonstrates testing with DynamoDB Local.

License

Notifications You must be signed in to change notification settings

moneyice/aws-sam-java-rest

 
 

Repository files navigation

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

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

  1. Start DynamoDB Local in a Docker container. docker run -p 8000:8000 amazon/dynamodb-local
  2. 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
  3. 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:

About

A sample REST application built on SAM and DynamoDB that demonstrates testing with DynamoDB Local.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Java 96.7%
  • Python 3.3%