Skip to content

Commit 41b53c1

Browse files
committed
Document idempotency response hook feature.
1 parent 75ede4c commit 41b53c1

File tree

1 file changed

+57
-3
lines changed

1 file changed

+57
-3
lines changed

docs/utilities/idempotency.md

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ times with the same parameters**. This makes idempotent operations safe to retry
5555
<aspectLibraries>
5656
<aspectLibrary>
5757
<groupId>software.amazon.lambda</groupId>
58-
<artifactId>powertools-idempotency-dynamodb</artifactId>
58+
<artifactId>powertools-idempotency-core</artifactId>
5959
</aspectLibrary>
6060
</aspectLibraries>
6161
</configuration>
@@ -584,20 +584,22 @@ IdempotencyConfig.builder()
584584
.withUseLocalCache(true)
585585
.withLocalCacheMaxItems(432)
586586
.withHashFunction("SHA-256")
587+
.withResponseHook((responseData, dataRecord) -> responseData)
587588
.build()
588589
```
589590

590591
These are the available options for further configuration:
591592

592593
| Parameter | Default | Description |
593594
|---------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------|
594-
| **EventKeyJMESPath** | `""` | JMESPath expression to extract the idempotency key from the event record. See available [built-in functions](serialization) |
595+
| **EventKeyJMESPath** | `""` | JMESPath expression to extract the idempotency key from the event record. See available [built-in functions](serialization) |
595596
| **PayloadValidationJMESPath** | `""` | JMESPath expression to validate whether certain parameters have changed in the event |
596-
| **ThrowOnNoIdempotencyKey** | `False` | Throw exception if no idempotency key was found in the request |
597+
| **ThrowOnNoIdempotencyKey** | `false` | Throw exception if no idempotency key was found in the request |
597598
| **ExpirationInSeconds** | 3600 | The number of seconds to wait before a record is expired |
598599
| **UseLocalCache** | `false` | Whether to locally cache idempotency results (LRU cache) |
599600
| **LocalCacheMaxItems** | 256 | Max number of items to store in local cache |
600601
| **HashFunction** | `MD5` | Algorithm to use for calculating hashes, as supported by `java.security.MessageDigest` (eg. SHA-1, SHA-256, ...) |
602+
| **ResponseHook** | `null` | Response hook to apply modifications to idempotent responses |
601603

602604
These features are detailed below.
603605

@@ -855,6 +857,58 @@ You can extend the `BasePersistenceStore` class and implement the abstract metho
855857

856858
For example, the `putRecord` method needs to throw an exception if a non-expired record already exists in the data store with a matching key.
857859

860+
### Manipulating the Idempotent Response
861+
862+
You can set up a response hook in the Idempotency configuration to manipulate the returned data when an operation is idempotent. The hook function will be called with the current de-serialized response `Object` and the Idempotency `DataRecord`.
863+
864+
The example below shows how to append an HTTP header to an `APIGatewayProxyResponseEvent`.
865+
866+
=== "Using an Idempotent Response Hook"
867+
868+
```java hl_lines="3-20"
869+
Idempotency.config().withConfig(
870+
IdempotencyConfig.builder()
871+
.withResponseHook((responseData, dataRecord) -> {
872+
if (responseData instanceof APIGatewayProxyResponseEvent) {
873+
APIGatewayProxyResponseEvent proxyResponse =
874+
(APIGatewayProxyResponseEvent) responseData;
875+
final Map<String, String> headers = new HashMap<>();
876+
headers.putAll(proxyResponse.getHeaders());
877+
// Append idempotency headers
878+
headers.put("x-idempotency-response", "true");
879+
headers.put("x-idempotency-expiration",
880+
String.valueOf(dataRecord.getExpiryTimestamp()));
881+
882+
proxyResponse.setHeaders(headers);
883+
884+
return proxyResponse;
885+
}
886+
887+
return responseData;
888+
})
889+
.build())
890+
.withPersistenceStore(
891+
DynamoDBPersistenceStore.builder()
892+
.withTableName(System.getenv("IDEMPOTENCY_TABLE"))
893+
.build())
894+
.configure();
895+
```
896+
897+
???+ info "Info: Using custom de-serialization?"
898+
899+
The response hook is called after de-serialization so the payload you process will be the de-serialized Java object.
900+
901+
#### Being a good citizen
902+
903+
When using response hooks to manipulate returned data from idempotent operations, it's important to follow best practices to avoid introducing complexity or issues. Keep these guidelines in mind:
904+
905+
1. **Response hook works exclusively when operations are idempotent.** The hook will not be called when an operation is not idempotent, or when the idempotent logic fails.
906+
907+
2. **Catch and Handle Exceptions.** Your response hook code should catch and handle any exceptions that may arise from your logic. Unhandled exceptions will cause the Lambda function to fail unexpectedly.
908+
909+
3. **Keep Hook Logic Simple** Response hooks should consist of minimal and straightforward logic for manipulating response data. Avoid complex conditional branching and aim for hooks that are easy to reason about.
910+
911+
858912
## Compatibility with other utilities
859913

860914
### Validation utility

0 commit comments

Comments
 (0)