Lambda & DynanoDB & Gateway

Deploying AWS Lambda, API Gateway and DynanoDB using AWS Serverless Application Model(SAM).

template.yaml

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: CoolantLambdaJava

Globals:
  Function:
    Runtime: java8
    MemorySize: 512
    Timeout: 5
    Environment:
      Variables:
        COOLANT_TABLE: !Ref CoolantTemperatureSensor
  Api:
    OpenApiVersion: '3.0.1'

Resources:

  CoolantTemperatureSensor:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: uid
        Type: String

  CoolantEventLambda:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: target/lambda.jar
      Handler: io.tiago.coolant.CoolantEventLambda::handler
      Policies:
        - DynamoDBCrudPolicy:
            TableName:
              !Ref CoolantTemperatureSensor
      Environment:
        Variables:
          ACCESS_KEY: 123A
          SECRET_KEY: X234
      Events:
        MyApi:
          Type: Api
          Properties:
            Path: /events
            Method: post

  CoolantQueryLambda:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: target/lambda.jar
      Handler: io.tiago.coolant.CoolantQueryLambda::handler
      Policies:
        - DynamoDBCrudPolicy:
            TableName:
              !Ref CoolantTemperatureSensor
      Environment:
        Variables:
          ACCESS_KEY: 123A
          SECRET_KEY: X234
      Events:
        MyApi:
          Type: Api
          Properties:
            Path: /events
            Method: get

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>io.tiago</groupId>
  <artifactId>coolant</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
      <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-s3</artifactId>
        <version>1.11.873</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-java-sdk-dynamodb</artifactId>
      <version>1.11.873</version>
    </dependency>
    <dependency>
      <groupId>com.amazonaws</groupId>
      <artifactId>aws-lambda-java-events</artifactId>
      <version>2.2.6</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <finalName>lambda</finalName>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

ApiGatewayRequest.java

package io.tiago.coolant;

import java.util.HashMap;

import java.util.Map;

public class ApiGatewayRequest {
    public String body;
    public Map<String, String> queryStringParameters = new HashMap<>();
}

ApiGatewayResponse.java

package io.tiago.coolant;

public class ApiGatewayResponse {

    public final int statusCode;

    public final String body;

    public ApiGatewayResponse(int statusCode, String body) {
        this.statusCode = statusCode;
        this.body = body;
    }
}

CoolantEventLambda.java

package io.tiago.coolant;

import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;
import com.amazonaws.services.dynamodbv2.model.ReturnConsumedCapacity;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class CoolantEventLambda {

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final String tableName = System.getenv("COOLANT_TABLE");

    public ApiGatewayResponse handler(ApiGatewayRequest request) throws IOException {

        final CoolantTemperatureSensor coolantTemperatureSensor = objectMapper.readValue(request.body, CoolantTemperatureSensor.class);

        PutItemRequest putItemRequest = new PutItemRequest();
        putItemRequest.setTableName(tableName);
        putItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);
        putItemRequest.setReturnValues(ReturnValue.ALL_OLD);

        Map<String, AttributeValue> map = new HashMap<>();
        map.put("uid", new AttributeValue(UUID.randomUUID().toString()));
        map.put("timestamp", (new AttributeValue()).withN(String.valueOf(System.currentTimeMillis())));
        map.put("temperature", (new AttributeValue(coolantTemperatureSensor.getTemperature())));
        putItemRequest.setItem(map);

        DynamoDB.getConn().putItem(putItemRequest);

        return new ApiGatewayResponse(200, "OK");
    }
}

CoolantQueryLambda.java

package io.tiago.coolant;

import com.amazonaws.services.dynamodbv2.model.ScanRequest;
import com.amazonaws.services.dynamodbv2.model.ScanResult;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

public class CoolantQueryLambda {

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final String tableName = System.getenv("LOCATIONS_TABLE");

    private static final String DEFAULT_LIMIT = "50";

    public ApiGatewayResponse handler(ApiGatewayRequest request) throws IOException {

        final String limitParam = request.queryStringParameters == null ? DEFAULT_LIMIT : request.queryStringParameters.getOrDefault("limit", DEFAULT_LIMIT);

        final int limit = Integer.parseInt(limitParam);

        final ScanRequest scanRequest = new ScanRequest().withTableName(tableName).withLimit(limit);

        final ScanResult scanResult = DynamoDB.getConn().scan(scanRequest);

        final List<CoolantTemperatureSensor> events = scanResult.getItems().stream().map(item -> new CoolantTemperatureSensor(
                item.get("uid").getS(),
                Long.valueOf(item.get("timestamp").getN()),
                item.get("temperature").getS()
        )).collect(Collectors.toList());

        final String json = objectMapper.writeValueAsString(events);

        return new ApiGatewayResponse(200, json);
    }
}

CoolantTemperatureSensor.java

package io.tiago.coolant;

public class CoolantTemperatureSensor {

    private String uid;

    private Long timestamp;

    private String temperature;

    public CoolantTemperatureSensor() {
    }

    public CoolantTemperatureSensor(String uid, Long timestamp, String temperature) {
        this.uid = uid;
        this.timestamp = timestamp;
        this.temperature = temperature;
    }

    public String getUid() {
        return uid;
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public String getTemperature() {
        return temperature;
    }

    public void setTemperature(String temperature) {
        this.temperature = temperature;
    }
}

DynamoDB.java

package io.tiago.coolant;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;

public class DynamoDB {

    public static AmazonDynamoDB getConn() {

        BasicAWSCredentials credentials = new BasicAWSCredentials(System.getenv("ACCESS_KEY"),
                                                                  System.getenv("SECRET_KEY"));

        return AmazonDynamoDBClientBuilder.standard()
                                          .withRegion(Regions.US_WEST_2)
                                          .withCredentials(new AWSStaticCredentialsProvider(credentials))
                                          .build();
    }
}