From 5de815e36dc88fa4a7203f31481f3a2bc1bedbb6 Mon Sep 17 00:00:00 2001 From: Philipp Page Date: Tue, 22 Apr 2025 20:53:33 +0200 Subject: [PATCH] feature(idempotency): Add support for ReturnValuesOnConditionCheckFailure in Idempotency. --- ...IdempotencyItemAlreadyExistsException.java | 15 ++++++ .../internal/IdempotencyHandler.java | 8 +-- .../idempotency/persistence/DataRecord.java | 19 +++++-- .../internal/IdempotencyAspectTest.java | 51 ++++++++++++++++--- .../dynamodb/DynamoDBPersistenceStore.java | 11 +++- .../persistence/dynamodb}/DynamoDBConfig.java | 16 +++--- .../DynamoDBPersistenceStoreTest.java | 26 ++++++---- .../dynamodb}/IdempotencyTest.java | 21 ++++---- .../handlers/IdempotencyFunction.java | 24 +++++---- 9 files changed, 136 insertions(+), 55 deletions(-) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/DynamoDBConfig.java (93%) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/IdempotencyTest.java (78%) rename powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/{software.amazon.lambda.powertools.idempotency.dynamodb => software/amazon/lambda/powertools/idempotency/persistence/dynamodb}/handlers/IdempotencyFunction.java (85%) diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java index ba7da69bf..aed2e5ae0 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/exceptions/IdempotencyItemAlreadyExistsException.java @@ -14,11 +14,17 @@ package software.amazon.lambda.powertools.idempotency.exceptions; +import java.util.Optional; + +import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; + /** * Exception thrown when trying to store an item which already exists. */ public class IdempotencyItemAlreadyExistsException extends RuntimeException { private static final long serialVersionUID = 9027152772149436500L; + // transient because we don't want to accidentally dump any payloads in logs / stack traces + private transient Optional dr = Optional.empty(); public IdempotencyItemAlreadyExistsException() { super(); @@ -27,4 +33,13 @@ public IdempotencyItemAlreadyExistsException() { public IdempotencyItemAlreadyExistsException(String msg, Throwable e) { super(msg, e); } + + public IdempotencyItemAlreadyExistsException(String msg, Throwable e, DataRecord dr) { + super(msg, e); + this.dr = Optional.ofNullable(dr); + } + + public Optional getDataRecord() { + return dr; + } } diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java index 38196b5d2..0466f244f 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyHandler.java @@ -94,9 +94,11 @@ private Object processIdempotency() throws Throwable { // already exists. If it succeeds, there's no need to call getRecord. persistenceStore.saveInProgress(data, Instant.now(), getRemainingTimeInMillis()); } catch (IdempotencyItemAlreadyExistsException iaee) { - DataRecord record = getIdempotencyRecord(); - if (record != null) { - return handleForStatus(record); + // If a DataRecord is already present on the Exception we can immediately take that one instead of trying + // to fetch it first. + DataRecord dr = iaee.getDataRecord().orElseGet(this::getIdempotencyRecord); + if (dr != null) { + return handleForStatus(dr); } } catch (IdempotencyKeyException ike) { throw ike; diff --git a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java index 2d56fe349..0621c372b 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java +++ b/powertools-idempotency/powertools-idempotency-core/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/DataRecord.java @@ -53,7 +53,7 @@ public class DataRecord { private final OptionalLong inProgressExpiryTimestamp; public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, - String payloadHash) { + String payloadHash) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -63,7 +63,7 @@ public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, St } public DataRecord(String idempotencyKey, Status status, long expiryTimestamp, String responseData, - String payloadHash, OptionalLong inProgressExpiryTimestamp) { + String payloadHash, OptionalLong inProgressExpiryTimestamp) { this.idempotencyKey = idempotencyKey; this.status = status.toString(); this.expiryTimestamp = expiryTimestamp; @@ -131,13 +131,22 @@ public int hashCode() { return Objects.hash(idempotencyKey, status, expiryTimestamp, responseData, payloadHash); } + @Override + public String toString() { + return "DataRecord{" + + "idempotencyKey='" + idempotencyKey + '\'' + + ", status='" + status + '\'' + + ", expiryTimestamp=" + expiryTimestamp + + ", payloadHash='" + payloadHash + '\'' + + '}'; + } /** * Status of the record: *
    - *
  • INPROGRESS: record initialized when function starts
  • - *
  • COMPLETED: record updated with the result of the function when it ends
  • - *
  • EXPIRED: record expired, idempotency will not happen
  • + *
  • INPROGRESS: record initialized when function starts
  • + *
  • COMPLETED: record updated with the result of the function when it ends
  • + *
  • EXPIRED: record expired, idempotency will not happen
  • *
*/ public enum Status { diff --git a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java index a434bcdc0..12113fc9e 100644 --- a/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java +++ b/powertools-idempotency/powertools-idempotency-core/src/test/java/software/amazon/lambda/powertools/idempotency/internal/IdempotencyAspectTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -154,7 +155,7 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce .build()) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); @@ -175,6 +176,44 @@ public void secondCall_notExpired_shouldGetFromStore() throws JsonProcessingExce assertThat(function.handlerCalled()).isFalse(); } + @Test + public void secondCall_notExpired_shouldNotGetFromStoreIfPresentOnIdempotencyException() + throws JsonProcessingException { + // GIVEN + Idempotency.config() + .withPersistenceStore(store) + .withConfig(IdempotencyConfig.builder() + .withEventKeyJMESPath("id") + .build()) + .configure(); + + Product p = new Product(42, "fake product", 12); + Basket b = new Basket(p); + DataRecord dr = new DataRecord( + "42", + DataRecord.Status.COMPLETED, + Instant.now().plus(356, SECONDS).getEpochSecond(), + JsonConfig.get().getObjectMapper().writer().writeValueAsString(b), + null); + + // A data record on this exception should take precedence over fetching a record from the store / cache + doThrow(new IdempotencyItemAlreadyExistsException( + "Test message", + new RuntimeException("Test Cause"), + dr)) + .when(store).saveInProgress(any(), any(), any()); + + // WHEN + IdempotencyEnabledFunction function = new IdempotencyEnabledFunction(); + Basket basket = function.handleRequest(p, context); + + // THEN + assertThat(basket).isEqualTo(b); + assertThat(function.handlerCalled()).isFalse(); + // Should never call the store because item is already present on IdempotencyItemAlreadyExistsException + verify(store, never()).getRecord(any(), any()); + } + @Test public void secondCall_notExpired_shouldGetStringFromStore() { // GIVEN @@ -185,7 +224,7 @@ public void secondCall_notExpired_shouldGetStringFromStore() { .build()) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); DataRecord dr = new DataRecord( @@ -220,7 +259,7 @@ public void secondCall_notExpired_shouldGetStringFromStoreWithResponseHook() { .build()) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); DataRecord dr = new DataRecord( @@ -251,7 +290,7 @@ public void secondCall_inProgress_shouldThrowIdempotencyAlreadyInProgressExcepti .build()) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); @@ -283,7 +322,7 @@ public void secondCall_inProgress_lambdaTimeout_timeoutExpired_shouldThrowIncons .build()) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); @@ -412,7 +451,7 @@ public void idempotencyOnSubMethodAnnotated_secondCall_notExpired_shouldGetFromS .withPersistenceStore(store) .configure(); - doThrow(IdempotencyItemAlreadyExistsException.class).when(store).saveInProgress(any(), any(), any()); + doThrow(new IdempotencyItemAlreadyExistsException()).when(store).saveInProgress(any(), any(), any()); Product p = new Product(42, "fake product", 12); Basket b = new Basket(p); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java index 0e20e396f..9c96541f6 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/main/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStore.java @@ -189,10 +189,17 @@ public void putRecord(DataRecord record, Instant now) throws IdempotencyItemAlre "attribute_not_exists(#id) OR #expiry < :now OR (attribute_exists(#in_progress_expiry) AND #in_progress_expiry < :now_milliseconds AND #status = :inprogress)") .expressionAttributeNames(expressionAttributeNames) .expressionAttributeValues(expressionAttributeValues) - .build() - ); + .returnValuesOnConditionCheckFailure("ALL_OLD") + .build()); } catch (ConditionalCheckFailedException e) { LOG.debug("Failed to put record for already existing idempotency key: {}", record.getIdempotencyKey()); + if (e.hasItem()) { + DataRecord existingRecord = itemToRecord(e.item()); + throw new IdempotencyItemAlreadyExistsException( + "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey() + + ". Existing record: " + existingRecord, + e, existingRecord); + } throw new IdempotencyItemAlreadyExistsException( "Failed to put record for already existing idempotency key: " + record.getIdempotencyKey(), e); } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java similarity index 93% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java index 30b4976d7..289b0f1cd 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/DynamoDBConfig.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBConfig.java @@ -12,15 +12,18 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; -import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; -import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; import java.io.IOException; import java.net.ServerSocket; import java.net.URI; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; + +import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; +import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; + import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; @@ -66,13 +69,12 @@ public static void setupDynamo() { .tableName(TABLE_NAME) .keySchema(KeySchemaElement.builder().keyType(KeyType.HASH).attributeName("id").build()) .attributeDefinitions( - AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build() - ) + AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build()) .billingMode(BillingMode.PAY_PER_REQUEST) .build()); - DescribeTableResponse response = - client.describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); + DescribeTableResponse response = client + .describeTable(DescribeTableRequest.builder().tableName(TABLE_NAME).build()); if (response == null) { throw new RuntimeException("Table was not created within expected time"); } diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java index cc682a81f..56b32c4f9 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/DynamoDBPersistenceStoreTest.java @@ -32,7 +32,7 @@ import software.amazon.awssdk.services.dynamodb.model.ScanRequest; import software.amazon.lambda.powertools.idempotency.Constants; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; -import software.amazon.lambda.powertools.idempotency.dynamodb.DynamoDBConfig; + import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemAlreadyExistsException; import software.amazon.lambda.powertools.idempotency.exceptions.IdempotencyItemNotFoundException; import software.amazon.lambda.powertools.idempotency.persistence.DataRecord; @@ -155,13 +155,14 @@ public void putRecord_shouldThrowIdempotencyItemAlreadyExistsException_IfRecordA DataRecord.Status.INPROGRESS, expiry2, null, - null - ), now) - ).isInstanceOf(IdempotencyItemAlreadyExistsException.class); + null), + now)).isInstanceOf(IdempotencyItemAlreadyExistsException.class) + // DataRecord should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") + .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); // THEN: item was not updated, retrieve the initial one - Map itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("COMPLETED"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); @@ -190,13 +191,16 @@ public void putRecord_shouldBlockUpdate_IfRecordAlreadyExistAndProgressNotExpire DataRecord.Status.INPROGRESS, expiry2, "Fake Data 2", - null - ), now)) - .isInstanceOf(IdempotencyItemAlreadyExistsException.class); + null), + now)) + .isInstanceOf(IdempotencyItemAlreadyExistsException.class) + // DataRecord should be present due to returnValuesOnConditionCheckFailure("ALL_OLD") + .matches(e -> ((IdempotencyItemAlreadyExistsException) e).getDataRecord().isPresent()); + ; // THEN: item was not updated, retrieve the initial one - Map itemInDb = - client.getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); + Map itemInDb = client + .getItem(GetItemRequest.builder().tableName(TABLE_NAME).key(key).build()).item(); assertThat(itemInDb).isNotNull(); assertThat(itemInDb.get("status").s()).isEqualTo("INPROGRESS"); assertThat(itemInDb.get("expiration").n()).isEqualTo(String.valueOf(expiry)); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java similarity index 78% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java index be915b610..7b43542c8 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/IdempotencyTest.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/IdempotencyTest.java @@ -12,20 +12,21 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb; - +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb; import static org.assertj.core.api.Assertions.assertThat; -import com.amazonaws.services.lambda.runtime.Context; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import com.amazonaws.services.lambda.runtime.tests.EventLoader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import com.amazonaws.services.lambda.runtime.tests.EventLoader; + import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.lambda.powertools.idempotency.dynamodb.handlers.IdempotencyFunction; +import software.amazon.lambda.powertools.idempotency.persistence.dynamodb.handlers.IdempotencyFunction; public class IdempotencyTest extends DynamoDBConfig { @@ -41,14 +42,14 @@ void setUp() { public void endToEndTest() { IdempotencyFunction function = new IdempotencyFunction(client); - APIGatewayProxyResponseEvent response = - function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response = function + .handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isTrue(); function.handlerExecuted = false; - APIGatewayProxyResponseEvent response2 = - function.handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); + APIGatewayProxyResponseEvent response2 = function + .handleRequest(EventLoader.loadApiGatewayRestEvent("apigw_event2.json"), context); assertThat(function.handlerExecuted).isFalse(); assertThat(response).isEqualTo(response2); diff --git a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java similarity index 85% rename from powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java rename to powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java index 227eea39e..d816af801 100644 --- a/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software.amazon.lambda.powertools.idempotency.dynamodb/handlers/IdempotencyFunction.java +++ b/powertools-idempotency/powertools-idempotency-dynamodb/src/test/java/software/amazon/lambda/powertools/idempotency/persistence/dynamodb/handlers/IdempotencyFunction.java @@ -12,14 +12,16 @@ * */ -package software.amazon.lambda.powertools.idempotency.dynamodb.handlers; +package software.amazon.lambda.powertools.idempotency.persistence.dynamodb.handlers; + +import java.util.HashMap; +import java.util.Map; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import java.util.HashMap; -import java.util.Map; + import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.lambda.powertools.idempotency.Idempotency; import software.amazon.lambda.powertools.idempotency.IdempotencyConfig; @@ -32,15 +34,15 @@ public class IdempotencyFunction implements RequestHandler