headers = new HashMap<>();
+ headers.putAll(proxyResponse.getHeaders());
+ headers.put("x-idempotency-response", "true");
+ headers.put("x-idempotency-expiration",
+ String.valueOf(dataRecord.getExpiryTimestamp()));
+
+ proxyResponse.setHeaders(headers);
+
+ return proxyResponse;
+ }
+
+ return responseData;
+ })
+ .build())
+ .withPersistenceStore(
+ DynamoDBPersistenceStore.builder()
+ .withDynamoDbClient(client)
+ .withTableName(System.getenv("IDEMPOTENCY_TABLE"))
+ .build())
+ .configure();
+ }
+
+ /**
+ * This is your Lambda event handler. It accepts HTTP POST requests from API gateway and returns the contents of the
+ * given URL. Requests are made idempotent
+ * by the idempotency library, and results are cached for the default 1h expiry time.
+ *
+ * You can test the endpoint like this:
+ *
+ *
+ * curl -X POST https://[REST-API-ID].execute-api.[REGION].amazonaws.com/Prod/helloidem/ -H "Content-Type: application/json" -d '{"address": "https://checkip.amazonaws.com"}'
+ *
+ *
+ * - First call will execute the handleRequest normally, and store the response in the idempotency table (Look
+ * into DynamoDB)
+ * - Second call (and next ones) will retrieve from the cache (if cache is enabled, which is by default) or from
+ * the store, the handler won't be called. Until the expiration happens (by default 1 hour).
+ *
+ */
+ @Idempotent // The magic is here!
+ @Logging(logEvent = true)
+ @Tracing
+ public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
+ Map headers = new HashMap<>();
+
+ headers.put("Content-Type", "application/json");
+ headers.put("Access-Control-Allow-Origin", "*");
+ headers.put("Access-Control-Allow-Methods", "GET, OPTIONS");
+ headers.put("Access-Control-Allow-Headers", "*");
+
+ APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
+ .withHeaders(headers);
+ try {
+ // Read the 'address' field from the JSON post body
+ String address = JsonConfig.get().getObjectMapper().readTree(input.getBody()).get("address").asText();
+ final String pageContents = this.getPageContents(address);
+ String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents);
+
+ log.info("ip is {}", pageContents);
+ return response
+ .withStatusCode(200)
+ .withBody(output);
+
+ } catch (IOException e) {
+ return response
+ .withBody("{}")
+ .withStatusCode(500);
+ }
+ }
+
+ /**
+ * Helper to retrieve the contents of the given URL and return them as a string.
+ *
+ * We could also put the @Idempotent annotation here if we only wanted this sub-operation to be idempotent. Putting
+ * it on the handler, however, reduces total execution time and saves us time!
+ *
+ * @param address
+ * The URL to fetch
+ * @return The contents of the given URL
+ * @throws IOException
+ */
+ @Tracing
+ private String getPageContents(String address) throws IOException {
+ URL url = new URL(address);
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8))) {
+ return br.lines().collect(Collectors.joining(System.lineSeparator()));
+ }
+ }
+}
diff --git a/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml b/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..5dede7b58
--- /dev/null
+++ b/examples/powertools-examples-idempotency/sam/src/main/resources/log4j2.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/powertools-examples-idempotency/template.yaml b/examples/powertools-examples-idempotency/sam/template.yaml
similarity index 100%
rename from examples/powertools-examples-idempotency/template.yaml
rename to examples/powertools-examples-idempotency/sam/template.yaml
diff --git a/pom.xml b/pom.xml
index f6ccb5056..bdafe88e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -459,6 +459,11 @@
maven-gpg-plugin
${maven-gpg-plugin.version}