Commit 3e6401a922f8e382e82d29589f4c8f42f3602712

Authored by Alex Mukha
1 parent e0815def12
Exists in master

Split the code into layers. Changed the expected request structure.

heartbeat-template.yaml
... ... @@ -7,7 +7,6 @@ Resources:
7 7 Properties:
8 8 Handler: org.alfresco.heartbeat.handler.HeartbeatRequestHandler
9 9 Runtime: java8
10   - Description: 'Heartbeat lambda'
11 10 MemorySize: 512
12 11 Timeout: 15
13 12 CodeUri: ./target/heartbeat-2.0-SNAPSHOT.jar
... ... @@ -29,18 +28,31 @@ Resources:
29 28 Properties:
30 29 KeySchema:
31 30 -
32   - AttributeName: "repositoryId"
  31 + AttributeName: "partitionKey"
33 32 KeyType: "HASH"
34 33 -
35   - AttributeName: "repositoryVersion"
  34 + AttributeName: "timestamp"
36 35 KeyType: "RANGE"
37 36 AttributeDefinitions:
38 37 -
39   - AttributeName: 'repositoryId'
  38 + AttributeName: 'partitionKey'
40 39 AttributeType: 'S'
41 40 -
42   - AttributeName: 'repositoryVersion'
  41 + AttributeName: 'timestamp'
43 42 AttributeType: 'S'
  43 +# Declare columns once the secondary indexes are described
  44 +# -
  45 +# AttributeName: "uuid"
  46 +# AttributeType: 'S'
  47 +# -
  48 +# AttributeName: "feature"
  49 +# AttributeType: 'S'
  50 +# -
  51 +# AttributeName: "version"
  52 +# AttributeType: 'S'
  53 +# -
  54 +# AttributeName: "payload"
  55 +# AttributeType: 'S'
44 56 ProvisionedThroughput:
45 57 ReadCapacityUnits: "5"
46 58 WriteCapacityUnits: "5"
... ...
src/main/java/org/alfresco/heartbeat/handler/HeartbeatRequestHandler.java
1 1 package org.alfresco.heartbeat.handler;
2 2  
3   -import com.amazonaws.regions.Region;
4   -import com.amazonaws.regions.Regions;
5   -import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
6   -import com.amazonaws.services.dynamodbv2.document.DynamoDB;
7   -import com.amazonaws.services.dynamodbv2.document.Item;
8   -import com.amazonaws.services.dynamodbv2.document.PutItemOutcome;
9   -import com.amazonaws.services.dynamodbv2.document.Table;
10 3 import com.amazonaws.services.lambda.runtime.Context;
11 4 import com.amazonaws.services.lambda.runtime.RequestHandler;
12   -import com.fasterxml.jackson.core.JsonFactory;
13   -import com.fasterxml.jackson.core.JsonParser;
14   -import com.fasterxml.jackson.databind.JsonNode;
15   -import com.fasterxml.jackson.databind.ObjectMapper;
16   -import com.fasterxml.jackson.databind.node.ObjectNode;
17   -import org.alfresco.heartbeat.HeartbeatRequest;
18   -import org.alfresco.heartbeat.HeartbeatResponse;
  5 +import org.alfresco.heartbeat.http.HeartbeatRequest;
  6 +import org.alfresco.heartbeat.http.HeartbeatResponse;
  7 +import org.alfresco.heartbeat.service.HeartbeatService;
19 8 import org.apache.http.HttpStatus;
20 9  
21   -import java.io.IOException;
22   -
23 10 /**
24 11 * Represents the receiver of the heart beat
25 12 *
... ... @@ -27,68 +14,25 @@ import java.io.IOException;
27 14 */
28 15 public class HeartbeatRequestHandler implements RequestHandler<HeartbeatRequest, HeartbeatResponse>
29 16 {
30   - private static final String TABLE_NAME = "TABLE_NAME";
31   - private static final String PRIMARY_KEY = "repositoryId";
32   - private static final String REPOSITORY_VERSION_KEY = "repositoryVersion";
  17 + private HeartbeatService heartbeatService;
33 18  
34 19 @Override
35 20 public HeartbeatResponse handleRequest(HeartbeatRequest request, Context context)
36 21 {
37   - String repositoryId = null;
38   - String repositoryVersion = null;
39 22 context.getLogger().log("Received object: " + request);
40   - ObjectMapper mapper = new ObjectMapper();
41   - if (request.getBody() == null)
42   - {
43   - return returnError(mapper);
44   - }
45   - JsonFactory factory = mapper.getFactory();
  23 + heartbeatService = HeartbeatService.getInstance();
  24 + HeartbeatResponse response = new HeartbeatResponse();
46 25 try
47 26 {
48   - JsonParser jp = factory.createParser(request.getBody());
49   - JsonNode actualObj = mapper.readTree(jp);
50   - repositoryId = actualObj.get(PRIMARY_KEY) == null ?
51   - null : actualObj.get(PRIMARY_KEY).asText();
52   - repositoryVersion = actualObj.get(REPOSITORY_VERSION_KEY) == null ?
53   - null : actualObj.get(REPOSITORY_VERSION_KEY).asText();
  27 + String responseBody = heartbeatService.saveData(request.getBody());
  28 + response.setStatusCode(HttpStatus.SC_CREATED);
  29 + response.setBody(responseBody);
54 30 }
55   - catch (IOException ioe)
56   - {
57   - context.getLogger().log("Could not parse request body.");
58   - return returnError(mapper);
59   - }
60   -
61   - if (repositoryId == null || repositoryVersion == null)
  31 + catch (Exception e)
62 32 {
63   - context.getLogger().log("Request is not valid.");
64   - return returnError(mapper);
  33 + context.getLogger().log("Processing failed: " + e.getMessage());
  34 + response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
65 35 }
66   -
67   - String tableName = System.getenv(TABLE_NAME);
68   - AmazonDynamoDBClient client = new AmazonDynamoDBClient();
69   - client.setRegion(Region.getRegion(Regions.EU_WEST_1));
70   - DynamoDB dynamoDB = new DynamoDB(client);
71   - Table table = dynamoDB.getTable(tableName);
72   - Item item = new Item()
73   - .withPrimaryKey(PRIMARY_KEY, repositoryId)
74   - .withString(REPOSITORY_VERSION_KEY, repositoryVersion);
75   - PutItemOutcome outcome = table.putItem(item);
76   -// TODO incorporate outcome in the response
77   - HeartbeatResponse response = new HeartbeatResponse();
78   - response.setStatusCode(HttpStatus.SC_CREATED);
79   - ObjectNode responseNode = mapper.createObjectNode();
80   - responseNode.put("success", true);
81   - response.setBody(responseNode.toString());
82   - return response;
83   - }
84   -
85   - private HeartbeatResponse returnError(ObjectMapper mapper)
86   - {
87   - HeartbeatResponse response = new HeartbeatResponse();
88   - response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
89   - ObjectNode responseNode = mapper.createObjectNode();
90   - responseNode.put("success", true);
91   - response.setBody(responseNode.toString());
92 36 return response;
93 37 }
94 38 }
... ...
src/main/java/org/alfresco/heartbeat/http/HeartbeatRequest.java
1 1 index bd89f601499d0cd32c9367441cecc8b97c69429d..588f805348cd9ed1fa8758a40a85032029a91f29 100644
2   --- a/src/main/java/org/alfresco/heartbeat/HeartbeatRequest.java
  2 +++ b/src/main/java/org/alfresco/heartbeat/http/HeartbeatRequest.java
1   -package org.alfresco.heartbeat;
  1 +package org.alfresco.heartbeat.http;
2 2  
3 3 import java.io.Serializable;
4 4  
... ...
src/main/java/org/alfresco/heartbeat/http/HeartbeatResponse.java
1 1 index 4d0d6abc0fedf0964276099c656f8c140cb4bd17..c3099ce69620a532d6aa1f23093dab9c3e00dded 100644
2   --- a/src/main/java/org/alfresco/heartbeat/HeartbeatResponse.java
  2 +++ b/src/main/java/org/alfresco/heartbeat/http/HeartbeatResponse.java
1   -package org.alfresco.heartbeat;
  1 +package org.alfresco.heartbeat.http;
2 2  
3 3 import java.io.Serializable;
4 4 import java.util.List;
... ...
src/main/java/org/alfresco/heartbeat/service/HeartbeatDAO.java
... ... @@ -0,0 +1,71 @@
  1 +package org.alfresco.heartbeat.service;
  2 +
  3 +import com.amazonaws.regions.Region;
  4 +import com.amazonaws.regions.Regions;
  5 +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
  6 +import com.amazonaws.services.dynamodbv2.document.DynamoDB;
  7 +import com.amazonaws.services.dynamodbv2.document.Item;
  8 +import com.amazonaws.services.dynamodbv2.document.PutItemOutcome;
  9 +import com.amazonaws.services.dynamodbv2.document.Table;
  10 +import com.fasterxml.jackson.databind.ObjectMapper;
  11 +import com.fasterxml.jackson.databind.node.ObjectNode;
  12 +
  13 +/**
  14 + * DAO layer
  15 + *
  16 + * @author amukha
  17 + */
  18 +public class HeartbeatDAO
  19 +{
  20 + private static final String TABLE_NAME_KEY = "TABLE_NAME";
  21 + private static final String PRIMARY_PARTITION_KEY = "partitionKey";
  22 + private static final String PRIMARY_SORT_KEY = "timestamp";
  23 + private static final String TIMESTAMP_KEY = PRIMARY_SORT_KEY;
  24 +
  25 + private Table table;
  26 +
  27 + /**
  28 + * The CF uses environment variable to set the table name to lambda during creation of the stack.
  29 + * This method will return the table name from the environment variable in case of amazon deployment.
  30 + * @return the table name from the environment variable of amazon deployment.
  31 + */
  32 + public String getTableName()
  33 + {
  34 + return System.getenv(TABLE_NAME_KEY);
  35 + }
  36 +
  37 + public HeartbeatDAO()
  38 + {
  39 + AmazonDynamoDBClient client = new AmazonDynamoDBClient();
  40 + client.setRegion(Region.getRegion(Regions.EU_WEST_1));
  41 + DynamoDB dynamoDB = new DynamoDB(client);
  42 + table = dynamoDB.getTable(getTableName());
  43 + }
  44 +
  45 + public PutItemOutcome putItem(String uuid,
  46 + String feature,
  47 + String version,
  48 + String timestamp,
  49 + String payload)
  50 + {
  51 + String primaryPartitionKey = createPrimaryPartitionKey(uuid, feature, timestamp);
  52 +
  53 + Item item = new Item()
  54 + .withPrimaryKey(PRIMARY_PARTITION_KEY, primaryPartitionKey)
  55 + .withString(TIMESTAMP_KEY, timestamp)
  56 + .withString(HeartbeatService.UUID_KEY, uuid)
  57 + .withString(HeartbeatService.FEATURE_KEY, feature)
  58 + .withString(HeartbeatService.VERSION_KEY, version)
  59 + .withString(HeartbeatService.PAYLOAD_KEY, payload);
  60 + return table.putItem(item);
  61 + }
  62 +
  63 + private String createPrimaryPartitionKey(String uuid, String feature, String timestamp)
  64 + {
  65 + ObjectMapper mapper = new ObjectMapper();
  66 + ObjectNode json = mapper.createObjectNode();
  67 + json.put(HeartbeatService.UUID_KEY, uuid);
  68 + json.put(HeartbeatService.FEATURE_KEY, feature);
  69 + return json.toString();
  70 + }
  71 +}
... ...
src/main/java/org/alfresco/heartbeat/service/HeartbeatService.java
... ... @@ -0,0 +1,97 @@
  1 +package org.alfresco.heartbeat.service;
  2 +
  3 +import com.fasterxml.jackson.databind.JsonNode;
  4 +import com.fasterxml.jackson.databind.ObjectMapper;
  5 +import com.fasterxml.jackson.databind.node.ObjectNode;
  6 +
  7 +import java.io.IOException;
  8 +
  9 +/**
  10 + * Service implementation
  11 + *
  12 + * @author amukha
  13 + */
  14 +public class HeartbeatService
  15 +{
  16 + static final String UUID_KEY = "uuid";
  17 + static final String FEATURE_KEY = "feature";
  18 + static final String VERSION_KEY = "version";
  19 + static final String PAYLOAD_KEY = "payload";
  20 +
  21 + private HeartbeatDAO heartbeatDAO;
  22 +
  23 + private static HeartbeatService instance;
  24 +
  25 + private HeartbeatService()
  26 + {
  27 + heartbeatDAO = new HeartbeatDAO();
  28 + }
  29 +
  30 + public static HeartbeatService getInstance()
  31 + {
  32 + if (instance == null)
  33 + {
  34 + instance = new HeartbeatService();
  35 + }
  36 + return instance;
  37 + }
  38 +
  39 + /**
  40 + * @param requestBody the body of the request as JSON containing all necessary data to save
  41 + * @return response body JSON as String
  42 + */
  43 + public String saveData(String requestBody) throws IOException
  44 + {
  45 + if (requestBody == null)
  46 + {
  47 + throw new IllegalArgumentException("The provided request body is null.");
  48 + }
  49 + ObjectMapper mapper = new ObjectMapper();
  50 + String uuid = null;
  51 + String feature = null;
  52 + String version = null;
  53 + String payload = null;
  54 + try
  55 + {
  56 + JsonNode actualObj = mapper.readTree(requestBody);
  57 + uuid = actualObj.get(UUID_KEY) == null ?
  58 + null : actualObj.get(UUID_KEY).asText();
  59 + feature = actualObj.get(FEATURE_KEY) == null ?
  60 + null : actualObj.get(FEATURE_KEY).asText();
  61 + version = actualObj.get(VERSION_KEY) == null ?
  62 + null : actualObj.get(VERSION_KEY).asText();
  63 + payload = actualObj.get(PAYLOAD_KEY) == null ?
  64 + null : actualObj.get(PAYLOAD_KEY).toString();
  65 + }
  66 + catch (IOException ioe)
  67 + {
  68 + throw ioe;
  69 + }
  70 + if (!isMandatoryDataValid(uuid, feature, version, payload))
  71 + {
  72 + throw new IllegalArgumentException("The request body is invalid." +
  73 + "\n Received: " + requestBody +
  74 + "\n Parsed: " +
  75 + UUID_KEY + " = " + uuid + ", " +
  76 + FEATURE_KEY + " = " + feature + ", " +
  77 + VERSION_KEY + " = " + version + ", " +
  78 + PAYLOAD_KEY + " = " + payload);
  79 + }
  80 + heartbeatDAO.putItem(uuid, feature, version, Long.toString(System.currentTimeMillis()), payload);
  81 + ObjectNode responseNode = mapper.createObjectNode();
  82 + responseNode.put("success", true);
  83 + return responseNode.toString();
  84 + }
  85 +
  86 +
  87 + private boolean isMandatoryDataValid(String uuid,
  88 + String feature,
  89 + String version,
  90 + String payload)
  91 + {
  92 + return !(uuid == null || uuid.isEmpty() ||
  93 + feature == null || feature.isEmpty() ||
  94 + version == null || version.isEmpty() ||
  95 + payload == null || payload.isEmpty());
  96 + }
  97 +}
... ...