diff --git a/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-96aff9e.json b/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-96aff9e.json
new file mode 100644
index 000000000000..8ed0b2c84583
--- /dev/null
+++ b/.changes/next-release/feature-AmazonDynamoDBEnhancedClient-96aff9e.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "Amazon DynamoDB Enhanced Client",
+ "contributor": "",
+ "description": "Added support for @DynamoDbAutoGeneratedTimestampAttribute and @DynamoDbUpdateBehavior on attributes within nested objects. The @DynamoDbUpdateBehavior annotation will only take effect for nested attributes when using IgnoreNullsMode.SCALAR_ONLY."
+}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedTimestampRecordExtension.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedTimestampRecordExtension.java
index 2ac27d918202..27bf8065a015 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedTimestampRecordExtension.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedTimestampRecordExtension.java
@@ -15,13 +15,21 @@
package software.amazon.awssdk.enhanced.dynamodb.extensions;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.getNestedSchema;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility.NestedRecordUtils.getTableSchemaForListElement;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility.NestedRecordUtils.reconstructCompositeKey;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility.NestedRecordUtils.resolveSchemasPerPath;
+
import java.time.Clock;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import software.amazon.awssdk.annotations.NotThreadSafe;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ThreadSafe;
@@ -30,6 +38,7 @@
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbExtensionContext;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTag;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableMetadata;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
@@ -64,6 +73,10 @@
*
* Every time a new update of the record is successfully written to the database, the timestamp at which it was modified will
* be automatically updated. This extension applies the conversions as defined in the attribute convertor.
+ * The implementation handles both flattened nested parameters (identified by keys separated with
+ * {@code "_NESTED_ATTR_UPDATE_"}) and entire nested maps or lists, ensuring consistent behavior across both representations.
+ * If a nested object or list is {@code null}, no timestamp values will be generated for any of its annotated fields.
+ * The same timestamp value is used for both top-level attributes and all applicable nested fields.
*/
@SdkPublicApi
@ThreadSafe
@@ -126,26 +139,103 @@ public static AutoGeneratedTimestampRecordExtension create() {
*/
@Override
public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
+ Map itemToTransform = new HashMap<>(context.items());
+
+ Map updatedItems = new HashMap<>();
+ Instant currentInstant = clock.instant();
+
+ itemToTransform.forEach((key, value) -> {
+ if (value.hasM() && value.m() != null) {
+ Optional extends TableSchema>> nestedSchema = getNestedSchema(context.tableSchema(), key);
+ if (nestedSchema.isPresent()) {
+ Map processed = processNestedObject(value.m(), nestedSchema.get(), currentInstant);
+ updatedItems.put(key, AttributeValue.builder().m(processed).build());
+ }
+ } else if (value.hasL() && !value.l().isEmpty() && value.l().get(0).hasM()) {
+ TableSchema> elementListSchema = getTableSchemaForListElement(context.tableSchema(), key);
+
+ List updatedList = value.l()
+ .stream()
+ .map(listItem -> listItem.hasM() ?
+ AttributeValue.builder()
+ .m(processNestedObject(listItem.m(),
+ elementListSchema,
+ currentInstant))
+ .build() : listItem)
+ .collect(Collectors.toList());
+ updatedItems.put(key, AttributeValue.builder().l(updatedList).build());
+ }
+ });
+
+ Map> stringTableSchemaMap = resolveSchemasPerPath(itemToTransform, context.tableSchema());
- Collection customMetadataObject = context.tableMetadata()
- .customMetadataObject(CUSTOM_METADATA_KEY, Collection.class).orElse(null);
+ stringTableSchemaMap.forEach((path, schema) -> {
+ Collection customMetadataObject = schema.tableMetadata()
+ .customMetadataObject(CUSTOM_METADATA_KEY, Collection.class)
+ .orElse(null);
- if (customMetadataObject == null) {
+ if (customMetadataObject != null) {
+ customMetadataObject.forEach(
+ key -> insertTimestampInItemToTransform(updatedItems, reconstructCompositeKey(path, key),
+ schema.converterForAttribute(key), currentInstant));
+ }
+ });
+
+ if (updatedItems.isEmpty()) {
return WriteModification.builder().build();
}
- Map itemToTransform = new HashMap<>(context.items());
- customMetadataObject.forEach(
- key -> insertTimestampInItemToTransform(itemToTransform, key,
- context.tableSchema().converterForAttribute(key)));
+
+ itemToTransform.putAll(updatedItems);
+
return WriteModification.builder()
.transformedItem(Collections.unmodifiableMap(itemToTransform))
.build();
}
+ private Map processNestedObject(Map nestedMap, TableSchema> nestedSchema,
+ Instant currentInstant) {
+ Map updatedNestedMap = new HashMap<>(nestedMap);
+ Collection customMetadataObject = nestedSchema.tableMetadata()
+ .customMetadataObject(CUSTOM_METADATA_KEY, Collection.class)
+ .orElse(null);
+
+ if (customMetadataObject != null) {
+ customMetadataObject.forEach(
+ key -> insertTimestampInItemToTransform(updatedNestedMap, String.valueOf(key),
+ nestedSchema.converterForAttribute(key), currentInstant));
+ }
+
+ nestedMap.forEach((nestedKey, nestedValue) -> {
+ if (nestedValue.hasM()) {
+ Optional extends TableSchema>> childSchemaOptional = getNestedSchema(nestedSchema, nestedKey);
+ TableSchema> schemaToUse = childSchemaOptional.isPresent() ? childSchemaOptional.get() : nestedSchema;
+ updatedNestedMap.put(nestedKey,
+ AttributeValue.builder()
+ .m(processNestedObject(nestedValue.m(), schemaToUse, currentInstant))
+ .build());
+
+ } else if (nestedValue.hasL() && !nestedValue.l().isEmpty() && nestedValue.l().get(0).hasM()) {
+ TableSchema> listElementSchema = getTableSchemaForListElement(nestedSchema, nestedKey);
+ List updatedList = nestedValue
+ .l()
+ .stream()
+ .map(listItem -> listItem.hasM() ?
+ AttributeValue.builder()
+ .m(processNestedObject(listItem.m(),
+ listElementSchema,
+ currentInstant)).build() : listItem)
+ .collect(Collectors.toList());
+ updatedNestedMap.put(nestedKey, AttributeValue.builder().l(updatedList).build());
+ }
+ });
+ return updatedNestedMap;
+ }
+
private void insertTimestampInItemToTransform(Map itemToTransform,
String key,
- AttributeConverter converter) {
- itemToTransform.put(key, converter.transformFrom(clock.instant()));
+ AttributeConverter converter,
+ Instant instant) {
+ itemToTransform.put(key, converter.transformFrom(instant));
}
/**
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/EnhancedClientUtils.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/EnhancedClientUtils.java
index 61d750e98a7e..3787e12a9a34 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/EnhancedClientUtils.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/EnhancedClientUtils.java
@@ -29,6 +29,7 @@
import java.util.stream.Stream;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
+import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
@@ -204,4 +205,24 @@ public static List getItemsFromSupplier(List> itemSupplierLis
public static boolean isNullAttributeValue(AttributeValue attributeValue) {
return attributeValue.nul() != null && attributeValue.nul();
}
+
+ /**
+ * Retrieves the {@link TableSchema} for a nested attribute within the given parent schema. When the attribute is a
+ * parameterized type (e.g., List>), it retrieves the schema of the first type parameter. Otherwise, it retrieves the schema
+ * directly from the attribute's enhanced type.
+ *
+ * @param parentSchema the schema of the parent bean class
+ * @param attributeName the name of the nested attribute
+ * @return an {@link Optional} containing the nested attribute's {@link TableSchema}, or empty if unavailable
+ */
+ public static Optional extends TableSchema>> getNestedSchema(TableSchema> parentSchema, String attributeName) {
+ EnhancedType> enhancedType = parentSchema.converterForAttribute(attributeName).type();
+ List> rawClassParameters = enhancedType.rawClassParameters();
+
+ if (rawClassParameters != null && !rawClassParameters.isEmpty()) {
+ enhancedType = rawClassParameters.get(0);
+ }
+
+ return enhancedType.tableSchema();
+ }
}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java
new file mode 100644
index 000000000000..b63b561fad37
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/extensions/utility/NestedRecordUtils.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility;
+
+import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.getNestedSchema;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.operations.UpdateItemOperation.NESTED_OBJECT_UPDATE;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.regex.Pattern;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
+import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
+
+@SdkInternalApi
+public final class NestedRecordUtils {
+
+ private static final Pattern NESTED_OBJECT_PATTERN = Pattern.compile(NESTED_OBJECT_UPDATE);
+
+ private NestedRecordUtils() {
+ }
+
+ /**
+ * Resolves and returns the {@link TableSchema} for the element type of a list attribute from the provided root schema.
+ *
+ * This method is useful when dealing with lists of nested objects in a DynamoDB-enhanced table schema, particularly in
+ * scenarios where the list is part of a flattened nested structure.
+ *
+ * If the provided key contains the nested object delimiter (e.g., {@code _NESTED_ATTR_UPDATE_}), the method traverses the
+ * nested hierarchy based on that path to locate the correct schema for the target attribute. Otherwise, it directly resolves
+ * the list element type from the root schema using reflection.
+ *
+ * @param rootSchema The root {@link TableSchema} representing the top-level entity.
+ * @param key The key representing the list attribute, either flat or nested (using a delimiter).
+ * @return The {@link TableSchema} representing the list element type of the specified attribute.
+ * @throws IllegalArgumentException If the list element class cannot be found via reflection.
+ */
+ public static TableSchema> getTableSchemaForListElement(TableSchema> rootSchema, String key) {
+ TableSchema> listElementSchema;
+ try {
+ if (!key.contains(NESTED_OBJECT_UPDATE)) {
+ Optional extends TableSchema>> staticSchema = getNestedSchema(rootSchema, key);
+ listElementSchema =
+ staticSchema.isPresent()
+ ? staticSchema.get()
+ : TableSchema.fromClass(Class.forName(
+ rootSchema.converterForAttribute(key).type().rawClassParameters().get(0).rawClass().getName()));
+
+ } else {
+ String[] parts = NESTED_OBJECT_PATTERN.split(key);
+ TableSchema> currentSchema = rootSchema;
+
+ for (int i = 0; i < parts.length - 1; i++) {
+ Optional extends TableSchema>> nestedSchema = getNestedSchema(currentSchema, parts[i]);
+ if (nestedSchema.isPresent()) {
+ currentSchema = nestedSchema.get();
+ }
+ }
+ String attributeName = parts[parts.length - 1];
+ listElementSchema = TableSchema.fromClass(
+ Class.forName(currentSchema.converterForAttribute(attributeName)
+ .type().rawClassParameters().get(0).rawClass().getName()));
+ }
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Class not found for field name: " + key, e);
+ }
+ return listElementSchema;
+ }
+
+ /**
+ * Traverses the attribute keys representing flattened nested structures and resolves the corresponding {@link TableSchema}
+ * for each nested path.
+ *
+ * The method constructs a mapping between each unique nested path (represented as dot-delimited strings) and the
+ * corresponding {@link TableSchema} object derived from the root schema. It supports resolving schemas for arbitrarily deep
+ * nesting, using the {@code _NESTED_ATTR_UPDATE_} pattern as a path delimiter.
+ *
+ * This is typically used in update or transformation flows where fields from nested objects are represented as flattened keys
+ * in the attribute map (e.g., {@code parent_NESTED_ATTR_UPDATE_child}).
+ *
+ * @param attributesToSet A map of flattened attribute keys to values, where keys may represent paths to nested attributes.
+ * @param rootSchema The root {@link TableSchema} of the top-level entity.
+ * @return A map where the key is the nested path (e.g., {@code "parent.child"}) and the value is the {@link TableSchema}
+ * corresponding to that level in the object hierarchy.
+ */
+ public static Map> resolveSchemasPerPath(Map attributesToSet,
+ TableSchema> rootSchema) {
+ Map> schemaMap = new HashMap<>();
+ schemaMap.put("", rootSchema);
+
+ for (String key : attributesToSet.keySet()) {
+ String[] parts = NESTED_OBJECT_PATTERN.split(key);
+
+ StringBuilder pathBuilder = new StringBuilder();
+ TableSchema> currentSchema = rootSchema;
+
+ for (int i = 0; i < parts.length - 1; i++) {
+ if (pathBuilder.length() > 0) {
+ pathBuilder.append(".");
+ }
+ pathBuilder.append(parts[i]);
+
+ String path = pathBuilder.toString();
+
+ if (!schemaMap.containsKey(path)) {
+ Optional extends TableSchema>> nestedSchema = getNestedSchema(currentSchema, parts[i]);
+ if (nestedSchema.isPresent()) {
+ schemaMap.put(path, nestedSchema.get());
+ currentSchema = nestedSchema.get();
+ }
+ } else {
+ currentSchema = schemaMap.get(path);
+ }
+ }
+ }
+ return schemaMap;
+ }
+
+ public static String reconstructCompositeKey(String path, String attributeName) {
+ if (path == null || path.isEmpty()) {
+ return attributeName;
+ }
+ return String.join(NESTED_OBJECT_UPDATE, path.split("\\."))
+ + NESTED_OBJECT_UPDATE + attributeName;
+ }
+}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/UpdateItemOperation.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/UpdateItemOperation.java
index 0ffe361b5aed..cd98db7417bc 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/UpdateItemOperation.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/UpdateItemOperation.java
@@ -132,7 +132,7 @@ public UpdateItemRequest generateRequest(TableSchema tableSchema,
Map keyAttributes = filterMap(itemMap, entry -> primaryKeys.contains(entry.getKey()));
Map nonKeyAttributes = filterMap(itemMap, entry -> !primaryKeys.contains(entry.getKey()));
- Expression updateExpression = generateUpdateExpressionIfExist(tableMetadata, transformation, nonKeyAttributes);
+ Expression updateExpression = generateUpdateExpressionIfExist(tableSchema, transformation, nonKeyAttributes);
Expression conditionExpression = generateConditionExpressionIfExist(transformation, request);
Map expressionNames = coalesceExpressionNames(updateExpression, conditionExpression);
@@ -275,7 +275,7 @@ public TransactWriteItem generateTransactWriteItem(TableSchema tableSchema, O
* if there are attributes to be updated (most likely). If both exist, they are merged and the code generates a final
* Expression that represent the result.
*/
- private Expression generateUpdateExpressionIfExist(TableMetadata tableMetadata,
+ private Expression generateUpdateExpressionIfExist(TableSchema tableSchema,
WriteModification transformation,
Map attributes) {
UpdateExpression updateExpression = null;
@@ -284,7 +284,7 @@ private Expression generateUpdateExpressionIfExist(TableMetadata tableMetadata,
}
if (!attributes.isEmpty()) {
List nonRemoveAttributes = UpdateExpressionConverter.findAttributeNames(updateExpression);
- UpdateExpression operationUpdateExpression = operationExpression(attributes, tableMetadata, nonRemoveAttributes);
+ UpdateExpression operationUpdateExpression = operationExpression(attributes, tableSchema, nonRemoveAttributes);
if (updateExpression == null) {
updateExpression = operationUpdateExpression;
} else {
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/update/UpdateExpressionUtils.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/update/UpdateExpressionUtils.java
index 1d47400ab2e6..4ad1989d057d 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/update/UpdateExpressionUtils.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/update/UpdateExpressionUtils.java
@@ -15,21 +15,24 @@
package software.amazon.awssdk.enhanced.dynamodb.internal.update;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.getNestedSchema;
import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.isNullAttributeValue;
import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.keyRef;
import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.valueRef;
import static software.amazon.awssdk.enhanced.dynamodb.internal.operations.UpdateItemOperation.NESTED_OBJECT_UPDATE;
import static software.amazon.awssdk.utils.CollectionUtils.filterMap;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import software.amazon.awssdk.annotations.SdkInternalApi;
-import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils;
import software.amazon.awssdk.enhanced.dynamodb.internal.mapper.UpdateBehaviorTag;
import software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior;
@@ -57,12 +60,12 @@ public static String ifNotExists(String key, String initValue) {
* Generates an UpdateExpression representing a POJO, with only SET and REMOVE actions.
*/
public static UpdateExpression operationExpression(Map itemMap,
- TableMetadata tableMetadata,
+ TableSchema tableSchema,
List nonRemoveAttributes) {
Map setAttributes = filterMap(itemMap, e -> !isNullAttributeValue(e.getValue()));
UpdateExpression setAttributeExpression = UpdateExpression.builder()
- .actions(setActionsFor(setAttributes, tableMetadata))
+ .actions(setActionsFor(setAttributes, tableSchema))
.build();
Map removeAttributes =
@@ -78,13 +81,31 @@ public static UpdateExpression operationExpression(Map i
/**
* Creates a list of SET actions for all attributes supplied in the map.
*/
- private static List setActionsFor(Map attributesToSet, TableMetadata tableMetadata) {
- return attributesToSet.entrySet()
- .stream()
- .map(entry -> setValue(entry.getKey(),
- entry.getValue(),
- UpdateBehaviorTag.resolveForAttribute(entry.getKey(), tableMetadata)))
- .collect(Collectors.toList());
+ private static List setActionsFor(Map attributesToSet, TableSchema tableSchema) {
+ List actions = new ArrayList<>();
+ for (Map.Entry entry : attributesToSet.entrySet()) {
+ String key = entry.getKey();
+ AttributeValue value = entry.getValue();
+
+ if (key.contains(NESTED_OBJECT_UPDATE)) {
+ TableSchema currentSchema = tableSchema;
+ List pathFieldNames = Arrays.asList(PATTERN.split(key));
+ String attributeName = pathFieldNames.get(pathFieldNames.size() - 1);
+
+ for (int i = 0; i < pathFieldNames.size() - 1; i++) {
+ Optional extends TableSchema>> nestedSchema = getNestedSchema(currentSchema, pathFieldNames.get(i));
+ if (nestedSchema.isPresent()) {
+ currentSchema = nestedSchema.get();
+ }
+ }
+
+ actions.add(setValue(key, value,
+ UpdateBehaviorTag.resolveForAttribute(attributeName, currentSchema.tableMetadata())));
+ } else {
+ actions.add(setValue(key, value, UpdateBehaviorTag.resolveForAttribute(key, tableSchema.tableMetadata())));
+ }
+ }
+ return actions;
}
/**
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbUpdateBehavior.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbUpdateBehavior.java
index fa161446c1a4..d14216b6a529 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbUpdateBehavior.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbUpdateBehavior.java
@@ -22,10 +22,15 @@
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.enhanced.dynamodb.internal.mapper.BeanTableSchemaAttributeTags;
import software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior;
+import software.amazon.awssdk.enhanced.dynamodb.model.IgnoreNullsMode;
/**
* Specifies the behavior when this attribute is updated as part of an 'update' operation such as UpdateItem. See
* documentation of {@link UpdateBehavior} for details on the different behaviors supported and the default behavior.
+ * For attributes within nested objects, this annotation is only respected when the request uses
+ * {@link IgnoreNullsMode#SCALAR_ONLY}. In {@link IgnoreNullsMode#MAPS_ONLY} or {@link IgnoreNullsMode#DEFAULT},
+ * the annotation has no effect. When applied to a list of nested objects, the annotation is not supported,
+ * as individual elements cannot be updated — the entire list is replaced during an update operation.
*/
@SdkPublicApi
@Target({ElementType.METHOD})
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/NestedRecordUtilsTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/NestedRecordUtilsTest.java
new file mode 100644
index 000000000000..c666b2644a8b
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/NestedRecordUtilsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.extensions;
+
+import static software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility.NestedRecordUtils.getTableSchemaForListElement;
+import static software.amazon.awssdk.enhanced.dynamodb.internal.extensions.utility.NestedRecordUtils.resolveSchemasPerPath;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.NestedRecordListElement;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.NestedRecordWithUpdateBehavior;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.RecordWithUpdateBehaviors;
+import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
+
+public class NestedRecordUtilsTest {
+
+ @Test
+ public void getTableSchemaForListElement_shouldReturnElementSchema() {
+ TableSchema parentSchema = TableSchema.fromBean(NestedRecordWithUpdateBehavior.class);
+
+ TableSchema> childSchema = getTableSchemaForListElement(parentSchema, "nestedRecordList");
+
+ Assertions.assertNotNull(childSchema);
+ Assertions.assertEquals(TableSchema.fromBean(NestedRecordListElement.class), childSchema);
+ }
+
+ @Test
+ public void resolveSchemasPerPath_shouldResolveNestedPaths() {
+ TableSchema rootSchema = TableSchema.fromBean(RecordWithUpdateBehaviors.class);
+
+ Map attributesToSet = new HashMap<>();
+ attributesToSet.put("nestedRecord_NESTED_ATTR_UPDATE_nestedRecord_NESTED_ATTR_UPDATE_attribute",
+ AttributeValue.builder().s("attributeValue").build());
+
+ Map> result = resolveSchemasPerPath(attributesToSet, rootSchema);
+
+ Assertions.assertEquals(3, result.size());
+ Assertions.assertTrue(result.containsKey(""));
+ Assertions.assertTrue(result.containsKey("nestedRecord"));
+ Assertions.assertTrue(result.containsKey("nestedRecord.nestedRecord"));
+ }
+}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampRecordTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampRecordTest.java
index 5d5ccf4fdb4b..e1f9835ec1e3 100644
--- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampRecordTest.java
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutoGeneratedTimestampRecordTest.java
@@ -15,10 +15,14 @@
package software.amazon.awssdk.enhanced.dynamodb.functionaltests;
-import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel2RecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel3RecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel4Record;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildStaticSchemaForNestedRecordWithList;
import static software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues.stringValue;
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;
import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.updateBehavior;
@@ -27,11 +31,9 @@
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
-import java.util.stream.IntStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -40,15 +42,12 @@
import org.mockito.Mockito;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
+import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.Expression;
-import software.amazon.awssdk.enhanced.dynamodb.OperationContext;
-import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.converters.EpochMillisFormatTestConverter;
import software.amazon.awssdk.enhanced.dynamodb.converters.TimeFormatUpdateTestConverter;
import software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedTimestampRecordExtension;
-import software.amazon.awssdk.enhanced.dynamodb.internal.extensions.DefaultDynamoDbExtensionContext;
-import software.amazon.awssdk.enhanced.dynamodb.internal.operations.DefaultOperationContext;
import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior;
import software.amazon.awssdk.enhanced.dynamodb.model.PutItemEnhancedRequest;
@@ -70,17 +69,13 @@ public class AutoGeneratedTimestampRecordTest extends LocalDynamoDbSyncTestBase
public static final Instant MOCKED_INSTANT_UPDATE_TWO = Instant.now(Clock.fixed(Instant.parse("2019-01-15T14:00:00Z"),
ZoneOffset.UTC));
- private static final String TABLE_NAME = "table-name";
- private static final OperationContext PRIMARY_CONTEXT =
- DefaultOperationContext.create(TABLE_NAME, TableMetadata.primaryIndexName());
-
private static final TableSchema FLATTENED_TABLE_SCHEMA =
StaticTableSchema.builder(FlattenedRecord.class)
.newItemSupplier(FlattenedRecord::new)
.addAttribute(Instant.class, a -> a.name("generated")
- .getter(FlattenedRecord::getGenerated)
- .setter(FlattenedRecord::setGenerated)
- .tags(autoGeneratedTimestampAttribute()))
+ .getter(FlattenedRecord::getGenerated)
+ .setter(FlattenedRecord::setGenerated)
+ .tags(autoGeneratedTimestampAttribute()))
.build();
private static final TableSchema TABLE_SCHEMA =
@@ -103,23 +98,23 @@ public class AutoGeneratedTimestampRecordTest extends LocalDynamoDbSyncTestBase
.tags(autoGeneratedTimestampAttribute(),
updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)))
.addAttribute(Instant.class, a -> a.name("lastUpdatedDateInEpochMillis")
- .getter(Record::getLastUpdatedDateInEpochMillis)
- .setter(Record::setLastUpdatedDateInEpochMillis)
- .attributeConverter(EpochMillisFormatTestConverter.create())
- .tags(autoGeneratedTimestampAttribute()))
+ .getter(Record::getLastUpdatedDateInEpochMillis)
+ .setter(Record::setLastUpdatedDateInEpochMillis)
+ .attributeConverter(EpochMillisFormatTestConverter.create())
+ .tags(autoGeneratedTimestampAttribute()))
.addAttribute(Instant.class, a -> a.name("convertedLastUpdatedDate")
.getter(Record::getConvertedLastUpdatedDate)
.setter(Record::setConvertedLastUpdatedDate)
.attributeConverter(TimeFormatUpdateTestConverter.create())
.tags(autoGeneratedTimestampAttribute()))
.flatten(FLATTENED_TABLE_SCHEMA, Record::getFlattenedRecord, Record::setFlattenedRecord)
+ .addAttribute(EnhancedType.documentOf(NestedStaticRecordWithList.class,
+ buildStaticSchemaForNestedRecordWithList(),
+ b -> b.ignoreNulls(true)),
+ a -> a.name("nestedRecord").getter(Record::getNestedRecord)
+ .setter(Record::setNestedRecord))
.build();
- private final List> fakeItems =
- IntStream.range(0, 4)
- .mapToObj($ -> createUniqueFakeItem())
- .map(fakeItem -> TABLE_SCHEMA.itemToMap(fakeItem, true))
- .collect(toList());
private final DynamoDbTable mappedTable;
private final Clock mockCLock = Mockito.mock(Clock.class);
@@ -160,39 +155,86 @@ public void deleteTable() {
}
@Test
- public void putNewRecordSetsInitialAutoGeneratedTimestamp() {
- Record item = new Record().setId("id").setAttribute("one");
+ public void putNewRecord_setsInitialTimestamps_onAllNestedBeanLevels() {
+ NestedStaticLevel4Record nestedLevel4 = new NestedStaticLevel4Record().setAttr("attrL4");
+ NestedStaticLevel3RecordWithList nestedLevel3 =
+ new NestedStaticLevel3RecordWithList().setAttr("attrL3").setLevel4(nestedLevel4);
+ NestedStaticLevel2RecordWithList level2 =
+ new NestedStaticLevel2RecordWithList().setAttr("attrL2").setLevel3(nestedLevel3);
+ NestedStaticRecordWithList nestedLevel1 = new NestedStaticRecordWithList().setAttr("attrL1").setLevel2(level2);
+
+ Record item = new Record()
+ .setId("id")
+ .setAttribute("one")
+ .setNestedRecord(nestedLevel1);
+
mappedTable.putItem(r -> r.item(item));
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
- GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
+ GetItemResponse stored = getItemAsStoredFromDDB();
+
+ NestedStaticLevel4Record expL4 = new NestedStaticLevel4Record()
+ .setTime(MOCKED_INSTANT_NOW).setAttr("attrL4");
+ NestedStaticLevel3RecordWithList expL3 = new NestedStaticLevel3RecordWithList()
+ .setTime(MOCKED_INSTANT_NOW).setAttr("attrL3").setLevel4(expL4);
+ NestedStaticLevel2RecordWithList expL2 = new NestedStaticLevel2RecordWithList()
+ .setTime(MOCKED_INSTANT_NOW).setAttr("attrL2").setLevel3(expL3);
+ NestedStaticRecordWithList expL1 = new NestedStaticRecordWithList()
+ .setTime(MOCKED_INSTANT_NOW).setAttr("attrL1").setLevel2(expL2);
+
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
- Record expectedRecord = new Record().setId("id")
- .setAttribute("one")
- .setLastUpdatedDate(MOCKED_INSTANT_NOW)
- .setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
- .setCreatedDate(MOCKED_INSTANT_NOW)
- .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
- .setFlattenedRecord(flattenedRecord);
+
+ Record expectedRecord = new Record()
+ .setId("id")
+ .setAttribute("one")
+ .setLastUpdatedDate(MOCKED_INSTANT_NOW)
+ .setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
+ .setCreatedDate(MOCKED_INSTANT_NOW)
+ .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
+ .setFlattenedRecord(flattenedRecord)
+ .setNestedRecord(expL1);
+
assertThat(result, is(expectedRecord));
- // The data in DDB is stored in converted time format
- assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
+ assertThat(stored.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
+
+ // nestedLevel1 assertions
+ Map lvl1Map = stored.item().get("nestedRecord").m();
+ assertThat(lvl1Map.get("time").s(), is(MOCKED_INSTANT_NOW.toString()));
+
+ // level2 assertions
+ Map lvl2Map = lvl1Map.get("level2").m();
+ assertThat(lvl2Map.get("time").s(), is(MOCKED_INSTANT_NOW.toString()));
+
+ // level3 assertions
+ Map lvl3Map = lvl2Map.get("level3").m();
+ assertThat(lvl3Map.get("time").s(), is(MOCKED_INSTANT_NOW.toString()));
+
+ // level4 assertions
+ Map lvl4Map = lvl3Map.get("level4").m();
+ assertThat(lvl4Map.get("time").s(), is(MOCKED_INSTANT_NOW.toString()));
}
@Test
public void updateNewRecordSetsAutoFormattedDate() {
- Record result = mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")));
+ Record result = mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList()
+ .setAttr("attribute"))));
GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_NOW);
+ NestedStaticRecordWithList expectednestedLevel1 = new NestedStaticRecordWithList().setTime(MOCKED_INSTANT_NOW)
+ .setAttr("attribute");
Record expectedRecord = new Record().setId("id")
.setAttribute("one")
.setLastUpdatedDate(MOCKED_INSTANT_NOW)
.setConvertedLastUpdatedDate(MOCKED_INSTANT_NOW)
.setCreatedDate(MOCKED_INSTANT_NOW)
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_NOW)
- .setFlattenedRecord(flattenedRecord);
+ .setFlattenedRecord(flattenedRecord)
+ .setNestedRecord(expectednestedLevel1);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("13 01 2019 14:00:00"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_NOW.toString()));
}
@Test
@@ -226,7 +268,7 @@ public void putExistingRecordUpdatedWithAutoFormattedTimestamps() {
.setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
.setFlattenedRecord(flattenedRecord);
- System.out.println("result "+result);
+ System.out.println("result " + result);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("14 01 2019 14:00:00"));
@@ -256,12 +298,12 @@ public void putItemFollowedByUpdates() {
itemAsStoredInDDB = getItemAsStoredFromDDB();
flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
expectedRecord = new Record().setId("id")
- .setAttribute("one")
- .setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
- .setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
- .setCreatedDate(MOCKED_INSTANT_NOW)
- .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
- .setFlattenedRecord(flattenedRecord);
+ .setAttribute("one")
+ .setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
+ .setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
+ .setCreatedDate(MOCKED_INSTANT_NOW)
+ .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
+ .setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
// The data in DDB is stored in converted time format
assertThat(itemAsStoredInDDB.item().get("convertedLastUpdatedDate").s(), is("14 01 2019 14:00:00"));
@@ -350,12 +392,12 @@ public void updateExistingRecordWithConditionExpressions() {
Record result = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
FlattenedRecord flattenedRecord = new FlattenedRecord().setGenerated(MOCKED_INSTANT_UPDATE_ONE);
Record expectedRecord = new Record().setId("id")
- .setAttribute("one")
- .setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
- .setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
- .setCreatedDate(MOCKED_INSTANT_NOW)
- .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
- .setFlattenedRecord(flattenedRecord);
+ .setAttribute("one")
+ .setLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
+ .setConvertedLastUpdatedDate(MOCKED_INSTANT_UPDATE_ONE)
+ .setCreatedDate(MOCKED_INSTANT_NOW)
+ .setLastUpdatedDateInEpochMillis(MOCKED_INSTANT_UPDATE_ONE)
+ .setFlattenedRecord(flattenedRecord);
assertThat(result, is(expectedRecord));
}
@@ -373,9 +415,9 @@ public void putItemConditionTestFailure() {
thrown.expect(ConditionalCheckFailedException.class);
mappedTable.putItem(PutItemEnhancedRequest.builder(Record.class)
- .item(new Record().setId("id").setAttribute("one"))
- .conditionExpression(conditionExpression)
- .build());
+ .item(new Record().setId("id").setAttribute("one"))
+ .conditionExpression(conditionExpression)
+ .build());
}
@@ -396,7 +438,7 @@ public void updateItemConditionTestFailure() {
}
@Test
- public void incorrectTypeForAutoUpdateTimestampThrowsException(){
+ public void incorrectTypeForAutoUpdateTimestampThrowsException() {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Attribute 'lastUpdatedDate' of Class type class java.lang.String is not a suitable "
@@ -415,6 +457,76 @@ public void incorrectTypeForAutoUpdateTimestampThrowsException(){
.build();
}
+ @Test
+ public void putItemFollowedByUpdatesShouldGenerateTimestampsOnNestedFields() {
+ mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr("attribute"))));
+ mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
+ GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_NOW.toString()));
+
+ //First Update
+ Mockito.when(mockCLock.instant()).thenReturn(MOCKED_INSTANT_UPDATE_ONE);
+
+ mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr(
+ "attribute1"))));
+ itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute1"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_UPDATE_ONE.toString()));
+
+ //Second Update
+ Mockito.when(mockCLock.instant()).thenReturn(MOCKED_INSTANT_UPDATE_TWO);
+ mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr(
+ "attribute2"))));
+ itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute2"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_UPDATE_TWO.toString()));
+ }
+
+ @Test
+ public void putItemFollowedByUpdatesShouldGenerateTimestampsOnNestedFieldsList() {
+ mappedTable.putItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr("attribute"))));
+ mappedTable.getItem(r -> r.key(k -> k.partitionValue("id")));
+ GetItemResponse itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_NOW.toString()));
+
+ //First Update
+ Mockito.when(mockCLock.instant()).thenReturn(MOCKED_INSTANT_UPDATE_ONE);
+
+ mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr(
+ "attribute1"))));
+ itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute1"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_UPDATE_ONE.toString()));
+
+ //Second Update
+ Mockito.when(mockCLock.instant()).thenReturn(MOCKED_INSTANT_UPDATE_TWO);
+ mappedTable.updateItem(r -> r.item(new Record().setId("id").setAttribute("one")
+ .setNestedRecord(new NestedStaticRecordWithList().setAttr(
+ "attribute2"))));
+ itemAsStoredInDDB = getItemAsStoredFromDDB();
+
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("attr").s(), is("attribute2"));
+ assertThat(itemAsStoredInDDB.item().get("nestedRecord").m().get("time").s(),
+ is(MOCKED_INSTANT_UPDATE_TWO.toString()));
+ }
+
private GetItemResponse getItemAsStoredFromDDB() {
Map key = new HashMap<>();
key.put("id", AttributeValue.builder().s("id").build());
@@ -424,6 +536,43 @@ private GetItemResponse getItemAsStoredFromDDB() {
.consistentRead(true).build());
}
+ private static class FlattenedRecord {
+ private Instant generated;
+
+ public Instant getGenerated() {
+ return generated;
+ }
+
+ public FlattenedRecord setGenerated(Instant generated) {
+ this.generated = generated;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FlattenedRecord that = (FlattenedRecord) o;
+ return Objects.equals(generated, that.generated);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(generated);
+ }
+
+ @Override
+ public String toString() {
+ return "FlattenedRecord{" +
+ "generated=" + generated +
+ '}';
+ }
+ }
+
private static class Record {
private String id;
private String attribute;
@@ -432,6 +581,7 @@ private static class Record {
private Instant convertedLastUpdatedDate;
private Instant lastUpdatedDateInEpochMillis;
private FlattenedRecord flattenedRecord;
+ private NestedStaticRecordWithList nestedLevel1;
private String getId() {
return id;
@@ -496,6 +646,15 @@ public Record setFlattenedRecord(FlattenedRecord flattenedRecord) {
return this;
}
+ public NestedStaticRecordWithList getNestedRecord() {
+ return nestedLevel1;
+ }
+
+ public Record setNestedRecord(NestedStaticRecordWithList nestedLevel1) {
+ this.nestedLevel1 = nestedLevel1;
+ return this;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -511,13 +670,14 @@ public boolean equals(Object o) {
Objects.equals(createdDate, record.createdDate) &&
Objects.equals(lastUpdatedDateInEpochMillis, record.lastUpdatedDateInEpochMillis) &&
Objects.equals(convertedLastUpdatedDate, record.convertedLastUpdatedDate) &&
- Objects.equals(flattenedRecord, record.flattenedRecord);
+ Objects.equals(flattenedRecord, record.flattenedRecord) &&
+ Objects.equals(nestedLevel1, record.nestedLevel1);
}
@Override
public int hashCode() {
return Objects.hash(id, attribute, lastUpdatedDate, createdDate, lastUpdatedDateInEpochMillis,
- convertedLastUpdatedDate, flattenedRecord);
+ convertedLastUpdatedDate, flattenedRecord, nestedLevel1);
}
@Override
@@ -530,43 +690,7 @@ public String toString() {
", convertedLastUpdatedDate=" + convertedLastUpdatedDate +
", lastUpdatedDateInEpochMillis=" + lastUpdatedDateInEpochMillis +
", flattenedRecord=" + flattenedRecord +
- '}';
- }
- }
-
- private static class FlattenedRecord {
- private Instant generated;
-
- public Instant getGenerated() {
- return generated;
- }
-
- public FlattenedRecord setGenerated(Instant generated) {
- this.generated = generated;
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- FlattenedRecord that = (FlattenedRecord) o;
- return Objects.equals(generated, that.generated);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(generated);
- }
-
- @Override
- public String toString() {
- return "FlattenedRecord{" +
- "generated=" + generated +
+ ", nestedRecord=" + nestedLevel1 +
'}';
}
}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutogeneratedTimestampTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutogeneratedTimestampTest.java
new file mode 100644
index 000000000000..1395c7e5bf51
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/AutogeneratedTimestampTest.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.functionaltests;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.hamcrest.CoreMatchers.everyItem;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasProperty;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_CHILD1;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_CHILD2;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_LEVEL1;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_LEVEL2;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_LEVEL3;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ATTR_LEVEL4;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.CHILD1_KEY;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.CHILD2_KEY;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.ID_1;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.LEVEL2_KEY;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedImmutableRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.SimpleImmutableRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.TIME_ATTR;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildBeanSchemaForNestedRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildBeanSchemaForSimpleRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildImmutableSchemaForNestedRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildImmutableSchemaForSimpleRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildNestedBeanRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildNestedBeanRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildNestedImmutableRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildNestedImmutableRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildNestedStaticRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildSimpleBeanRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildSimpleBeanRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildSimpleImmutableRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildSimpleImmutableRecordWithMap;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildSimpleStaticRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildStaticImmutableSchemaForNestedRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildStaticImmutableSchemaForSimpleRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildStaticSchemaForNestedRecordWithList;
+import static software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.buildStaticSchemaForSimpleRecordWithList;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mockito;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
+import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedTimestampRecordExtension;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedBeanLevel2RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedBeanLevel3RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedBeanLevel4Record;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedBeanRecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedBeanRecordWithMap;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedImmutableLevel2RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedImmutableLevel3RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedImmutableLevel4Record;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedImmutableRecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel2RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel3RecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticLevel4Record;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.NestedStaticRecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.SimpleBeanRecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.SimpleBeanRecordWithMap;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.SimpleImmutableRecordWithList;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.AutogeneratedTimestampModels.SimpleStaticRecordWithList;
+import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
+
+@RunWith(Parameterized.class)
+public class AutogeneratedTimestampTest extends LocalDynamoDbSyncTestBase {
+
+ // Test configuration constants
+ private static final String TIMESTAMP_STRING = "2019-01-13T14:00:00Z";
+ private static final String INVALID_SCHEMA_ERROR = "Invalid table schema";
+
+ // Table name suffixes
+ private static final String BASE_TABLE_NAME = "autogen-timestamp-test";
+ private static final String SIMPLE_BEAN_TABLE_SUFFIX = "-simple-bean-";
+ private static final String NESTED_BEAN_TABLE_SUFFIX = "-nested-bean-";
+ private static final String SIMPLE_IMMUTABLE_TABLE_SUFFIX = "-simple-immutable-";
+ private static final String NESTED_IMMUTABLE_TABLE_SUFFIX = "-nested-immutable-";
+ private static final String SIMPLE_STATIC_TABLE_SUFFIX = "-simple-static-";
+ private static final String NESTED_STATIC_TABLE_SUFFIX = "-nested-static-";
+ private static final String SIMPLE_STATIC_IMMUTABLE_TABLE_SUFFIX = "-simple-static-immutable-";
+ private static final String NESTED_STATIC_IMMUTABLE_TABLE_SUFFIX = "-nested-static-immutable-";
+
+ private static final Clock mockClock = Mockito.mock(Clock.class);
+ private static final Instant MOCKED_INSTANT_NOW =
+ Instant.now(Clock.fixed(Instant.parse(TIMESTAMP_STRING), ZoneOffset.UTC));
+ private DynamoDbEnhancedClient enhancedClient;
+ private String currentTestTableName;
+
+ private enum RecordLevel {SIMPLE, NESTED}
+
+ private enum SchemaType {BEAN, IMMUTABLE, STATIC, STATIC_IMMUTABLE}
+
+ @Parameterized.Parameters(name = "{0}-{1}")
+ public static Collection data() {
+ return Arrays.stream(SchemaType.values())
+ .flatMap(schema -> Arrays.stream(RecordLevel.values())
+ .map(level -> new Object[] {schema, level}))
+ .collect(Collectors.toList());
+ }
+
+ @Parameterized.Parameter(0)
+ public SchemaType schemaType;
+
+ @Parameterized.Parameter(1)
+ public RecordLevel recordLevel;
+
+ @Before
+ public void beforeClass() {
+ Mockito.when(mockClock.instant()).thenReturn(MOCKED_INSTANT_NOW);
+ enhancedClient = DynamoDbEnhancedClient.builder()
+ .dynamoDbClient(getDynamoDbClient())
+ .extensions(AutoGeneratedTimestampRecordExtension.builder()
+ .baseClock(mockClock)
+ .build())
+ .build();
+ }
+
+ @After
+ public void deleteTable() {
+ try {
+ if (currentTestTableName != null) {
+ getDynamoDbClient().deleteTable(r -> r.tableName(currentTestTableName));
+ }
+ } catch (ResourceNotFoundException e) {
+ // Table didn't get created, ignore.
+ }
+ }
+
+
+ @Test
+ public void shouldPopulateTimestamps_forRecordWithList() {
+ getTestCaseForRecordWithList(schemaType, recordLevel).run();
+ }
+
+ @Test
+ public void shouldThrowException_forRecordWithSet() {
+ if (schemaType == SchemaType.BEAN || schemaType == SchemaType.IMMUTABLE) {
+ assertThatThrownBy(() -> getTestCaseForRecordWithSet(schemaType, recordLevel).run())
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Converter not found for EnhancedType(java.util.Set<");
+ } else {
+ assertThatThrownBy(() -> getTestCaseForRecordWithSet(schemaType, recordLevel).run())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("SetAttributeConverter cannot be created with a parameterized type of '");
+ }
+ }
+
+ @Test
+ public void shouldThrowException_forRecordWithMap() {
+ if (schemaType == SchemaType.BEAN || schemaType == SchemaType.IMMUTABLE) {
+ assertThatThrownBy(() -> getTestCaseForRecordWithMap(schemaType, recordLevel).run())
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expected: is <2019-01-13T14:00:00Z>\n but: was null");
+ } else {
+ assertThatThrownBy(() -> getTestCaseForRecordWithMap(schemaType, recordLevel).run())
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("Converter not found for EnhancedType(java.util.Map {
+ throw new IllegalArgumentException(INVALID_SCHEMA_ERROR);
+ };
+ }
+ }
+
+ private Runnable getTestCaseForRecordWithSet(SchemaType schema, RecordLevel level) {
+ switch (schema) {
+ case BEAN:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildBeanSchemaForSimpleRecordWithSet
+ : AutogeneratedTimestampModels::buildBeanSchemaForNestedRecordWithSet;
+ case IMMUTABLE:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildImmutableSchemaForSimpleRecordWithSet
+ : AutogeneratedTimestampModels::buildImmutableSchemaForNestedRecordWithSet;
+ case STATIC:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildStaticSchemaForSimpleRecordWithSet
+ : AutogeneratedTimestampModels::buildStaticSchemaForNestedRecordWithSet;
+ case STATIC_IMMUTABLE:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildStaticImmutableSchemaForSimpleRecordWithSet
+ : AutogeneratedTimestampModels::buildStaticImmutableSchemaForNestedRecordWithSet;
+ default:
+ return () -> {
+ throw new IllegalArgumentException(INVALID_SCHEMA_ERROR);
+ };
+ }
+ }
+
+ private Runnable getTestCaseForRecordWithMap(SchemaType schema, RecordLevel level) {
+ switch (schema) {
+ case BEAN:
+ return (level == RecordLevel.SIMPLE)
+ ? this::testAutogeneratedTimestamp_givenBeanSchema_onSimpleRecordWithMap_throwsException
+ : this::testAutogeneratedTimestamp_givenBeanSchema_onNestedRecordWithMap_throwsException;
+ case IMMUTABLE:
+ return (level == RecordLevel.SIMPLE)
+ ? this::testAutogeneratedTimestamp_givenImmutableSchema_onSimpleRecordWithMap_throwsException
+ : this::testAutogeneratedTimestamp_givenImmutableSchema_onNestedRecordWithMap_throwsException;
+ case STATIC:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildStaticSchemaForSimpleRecordWithMap
+ : AutogeneratedTimestampModels::buildStaticSchemaForNestedRecordWithMap;
+ case STATIC_IMMUTABLE:
+ return (level == RecordLevel.SIMPLE)
+ ? AutogeneratedTimestampModels::buildStaticImmutableSchemaForSimpleRecordWithMap
+ : AutogeneratedTimestampModels::buildStaticImmutableSchemaForNestedRecordWithMap;
+ default:
+ return () -> {
+ throw new IllegalArgumentException(INVALID_SCHEMA_ERROR);
+ };
+ }
+ }
+
+
+ // Bean table schema + Record with List
+ private void testAutogeneratedTimestamp_givenBeanSchema_onSimpleRecordWithList_populatesTimestamps() {
+ TableSchema schema = buildBeanSchemaForSimpleRecordWithList();
+ DynamoDbTable table = createAndPut(SIMPLE_BEAN_TABLE_SUFFIX,
+ schema,
+ buildSimpleBeanRecordWithList());
+
+ SimpleBeanRecordWithList result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildList());
+ assertThat(result.getChildList().get(0).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildList().get(0).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildList().get(1).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildList().get(1).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenBeanSchema_onNestedRecordWithList_populatesTimestamps() {
+ TableSchema schema = buildBeanSchemaForNestedRecordWithList();
+ DynamoDbTable table = createAndPut(NESTED_BEAN_TABLE_SUFFIX,
+ schema,
+ buildNestedBeanRecordWithList());
+
+ NestedBeanRecordWithList level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2List(), notNullValue());
+ assertThat(level1.getLevel2List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedBeanLevel2RecordWithList level2 = level1.getLevel2();
+ assertThat(level2, notNullValue());
+ assertThat(level2.getAttr(), is(ATTR_LEVEL2));
+ assertThat(level2.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3(), notNullValue());
+ assertThat(level2.getLevel3().getAttr(), is(ATTR_LEVEL3));
+ assertThat(level2.getLevel3().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3List(), notNullValue());
+ assertThat(level2.getLevel3List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedBeanLevel3RecordWithList level3 = level2.getLevel3();
+ assertThat(level3, notNullValue());
+ assertThat(level3.getAttr(), is(ATTR_LEVEL3));
+ assertThat(level3.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4(), notNullValue());
+ assertThat(level3.getLevel4().getAttr(), is(ATTR_LEVEL4));
+ assertThat(level3.getLevel4().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4List(), notNullValue());
+ assertThat(level3.getLevel4List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedBeanLevel4Record level4 = level3.getLevel4();
+ assertThat(level4, notNullValue());
+ assertThat(level4.getAttr(), is(ATTR_LEVEL4));
+ assertThat(level4.getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ // Bean table schema + Record with Map
+ private void testAutogeneratedTimestamp_givenBeanSchema_onSimpleRecordWithMap_throwsException() {
+ TableSchema schema = AutogeneratedTimestampModels.buildBeanSchemaForSimpleRecordWithMap();
+ DynamoDbTable table = createAndPut(SIMPLE_BEAN_TABLE_SUFFIX,
+ schema,
+ buildSimpleBeanRecordWithMap());
+
+ SimpleBeanRecordWithMap result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildMap());
+ assertThat(result.getChildMap().get(CHILD1_KEY).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildMap().get(CHILD1_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildMap().get(CHILD2_KEY).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildMap().get(CHILD2_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenBeanSchema_onNestedRecordWithMap_throwsException() {
+ TableSchema schema = AutogeneratedTimestampModels.buildBeanSchemaForNestedRecordWithMap();
+ DynamoDbTable table = createAndPut(SIMPLE_BEAN_TABLE_SUFFIX,
+ schema,
+ buildNestedBeanRecordWithMap());
+
+ NestedBeanRecordWithMap level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertNotNull(level1.getLevel2Map());
+ assertThat(level1.getLevel2Map().get(LEVEL2_KEY).getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2Map().get(LEVEL2_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ // Immutable table schema + Record with Map
+ private void testAutogeneratedTimestamp_givenImmutableSchema_onSimpleRecordWithMap_throwsException() {
+ TableSchema schema =
+ AutogeneratedTimestampModels.buildImmutableSchemaForSimpleRecordWithMap();
+ DynamoDbTable table = createAndPut(SIMPLE_IMMUTABLE_TABLE_SUFFIX,
+ schema,
+ buildSimpleImmutableRecordWithMap());
+
+ SimpleImmutableRecordWithMap result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildMap());
+ assertThat(result.getChildMap().get(CHILD1_KEY).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildMap().get(CHILD1_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildMap().get(CHILD2_KEY).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildMap().get(CHILD2_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenImmutableSchema_onNestedRecordWithMap_throwsException() {
+ TableSchema schema =
+ AutogeneratedTimestampModels.buildImmutableSchemaForNestedRecordWithMap();
+ DynamoDbTable table = createAndPut(NESTED_IMMUTABLE_TABLE_SUFFIX,
+ schema,
+ buildNestedImmutableRecordWithMap());
+
+ NestedImmutableRecordWithMap level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertNotNull(level1.getLevel2Map());
+ assertThat(level1.getLevel2Map().get(LEVEL2_KEY).getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2Map().get(LEVEL2_KEY).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ // Immutable table schema + Record with List
+ private void testAutogeneratedTimestamp_givenImmutableSchema_onSimpleRecordWithList_populatesTimestamps() {
+ TableSchema schema = buildImmutableSchemaForSimpleRecordWithList();
+ DynamoDbTable table = createAndPut(SIMPLE_IMMUTABLE_TABLE_SUFFIX,
+ schema,
+ buildSimpleImmutableRecordWithList());
+
+ SimpleImmutableRecordWithList result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildList());
+ assertThat(result.getChildList().get(0).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildList().get(0).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildList().get(1).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildList().get(1).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenImmutableSchema_onNestedRecordWithList_populatesTimestamps() {
+ TableSchema schema = buildImmutableSchemaForNestedRecordWithList();
+ DynamoDbTable table = createAndPut(NESTED_IMMUTABLE_TABLE_SUFFIX,
+ schema,
+ buildNestedImmutableRecordWithList());
+
+ NestedImmutableRecordWithList level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2List(), notNullValue());
+ assertThat(level1.getLevel2List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel2RecordWithList level2 = level1.getLevel2();
+ assertThat(level2, notNullValue());
+ assertThat(level2.getAttr(), is(ATTR_LEVEL2));
+ assertThat(level2.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3(), notNullValue());
+ assertThat(level2.getLevel3().getAttr(), is(ATTR_LEVEL3));
+ assertThat(level2.getLevel3().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3List(), notNullValue());
+ assertThat(level2.getLevel3List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel3RecordWithList level3 = level2.getLevel3();
+ assertThat(level3, notNullValue());
+ assertThat(level3.getAttr(), is(ATTR_LEVEL3));
+ assertThat(level3.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4(), notNullValue());
+ assertThat(level3.getLevel4().getAttr(), is(ATTR_LEVEL4));
+ assertThat(level3.getLevel4().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4List(), notNullValue());
+ assertThat(level3.getLevel4List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel4Record level4 = level3.getLevel4();
+ assertThat(level4, notNullValue());
+ assertThat(level4.getAttr(), is(ATTR_LEVEL4));
+ assertThat(level4.getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ // Static table schema + Record with List
+ private void testAutogeneratedTimestamp_givenStaticSchema_onSimpleRecordWithList_populatesTimestamps() {
+ DynamoDbTable table = createAndPut(SIMPLE_STATIC_TABLE_SUFFIX,
+ buildStaticSchemaForSimpleRecordWithList(),
+ buildSimpleStaticRecordWithList());
+
+ SimpleStaticRecordWithList result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildList());
+ assertThat(result.getChildList().get(0).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildList().get(0).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildList().get(1).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildList().get(1).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenStaticImmutableSchema_onSimpleRecordWithList_populatesTimestamps() {
+ DynamoDbTable table = createAndPut(SIMPLE_STATIC_IMMUTABLE_TABLE_SUFFIX,
+ buildStaticImmutableSchemaForSimpleRecordWithList(),
+ buildSimpleImmutableRecordWithList());
+
+ SimpleImmutableRecordWithList result = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(result.getAttr(), is(ATTR_LEVEL1));
+ assertThat(result.getTime(), is(MOCKED_INSTANT_NOW));
+
+ assertNotNull(result.getChildList());
+ assertThat(result.getChildList().get(0).getAttr(), is(ATTR_CHILD1));
+ assertThat(result.getChildList().get(0).getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(result.getChildList().get(1).getAttr(), is(ATTR_CHILD2));
+ assertThat(result.getChildList().get(1).getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenStaticSchema_onNestedRecordWithList_populatesTimestamps() {
+ DynamoDbTable table = createAndPut(NESTED_STATIC_TABLE_SUFFIX,
+ buildStaticSchemaForNestedRecordWithList(),
+ buildNestedStaticRecordWithList());
+
+ NestedStaticRecordWithList level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2List(), notNullValue());
+ assertThat(level1.getLevel2List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedStaticLevel2RecordWithList level2 = level1.getLevel2();
+ assertThat(level2, notNullValue());
+ assertThat(level2.getAttr(), is(ATTR_LEVEL2));
+ assertThat(level2.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3(), notNullValue());
+ assertThat(level2.getLevel3().getAttr(), is(ATTR_LEVEL3));
+ assertThat(level2.getLevel3().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3List(), notNullValue());
+ assertThat(level2.getLevel3List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedStaticLevel3RecordWithList level3 = level2.getLevel3();
+ assertThat(level3, notNullValue());
+ assertThat(level3.getAttr(), is(ATTR_LEVEL3));
+ assertThat(level3.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4(), notNullValue());
+ assertThat(level3.getLevel4().getAttr(), is(ATTR_LEVEL4));
+ assertThat(level3.getLevel4().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4List(), notNullValue());
+ assertThat(level3.getLevel4List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedStaticLevel4Record level4 = level3.getLevel4();
+ assertThat(level4, notNullValue());
+ assertThat(level4.getAttr(), is(ATTR_LEVEL4));
+ assertThat(level4.getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ private void testAutogeneratedTimestamp_givenStaticImmutableSchema_onNestedRecordWithList_populatesTimestamps() {
+ DynamoDbTable table = createAndPut(NESTED_STATIC_IMMUTABLE_TABLE_SUFFIX,
+ buildStaticImmutableSchemaForNestedRecordWithList(),
+ buildNestedImmutableRecordWithList());
+
+ NestedImmutableRecordWithList level1 = table.getItem(r -> r.key(k -> k.partitionValue(ID_1)));
+
+ assertThat(level1, notNullValue());
+ assertThat(level1.getAttr(), is(ATTR_LEVEL1));
+ assertThat(level1.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2(), notNullValue());
+ assertThat(level1.getLevel2().getAttr(), is(ATTR_LEVEL2));
+ assertThat(level1.getLevel2().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level1.getLevel2List(), notNullValue());
+ assertThat(level1.getLevel2List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel2RecordWithList level2 = level1.getLevel2();
+ assertThat(level2, notNullValue());
+ assertThat(level2.getAttr(), is(ATTR_LEVEL2));
+ assertThat(level2.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3(), notNullValue());
+ assertThat(level2.getLevel3().getAttr(), is(ATTR_LEVEL3));
+ assertThat(level2.getLevel3().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level2.getLevel3List(), notNullValue());
+ assertThat(level2.getLevel3List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel3RecordWithList level3 = level2.getLevel3();
+ assertThat(level3, notNullValue());
+ assertThat(level3.getAttr(), is(ATTR_LEVEL3));
+ assertThat(level3.getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4(), notNullValue());
+ assertThat(level3.getLevel4().getAttr(), is(ATTR_LEVEL4));
+ assertThat(level3.getLevel4().getTime(), is(MOCKED_INSTANT_NOW));
+ assertThat(level3.getLevel4List(), notNullValue());
+ assertThat(level3.getLevel4List(), everyItem(hasProperty(TIME_ATTR, is(MOCKED_INSTANT_NOW))));
+
+ NestedImmutableLevel4Record level4 = level3.getLevel4();
+ assertThat(level4, notNullValue());
+ assertThat(level4.getAttr(), is(ATTR_LEVEL4));
+ assertThat(level4.getTime(), is(MOCKED_INSTANT_NOW));
+ }
+
+ // Helper for table creation + item insert
+ private DynamoDbTable createAndPut(String tableSuffix, TableSchema schema, T item) {
+ currentTestTableName = BASE_TABLE_NAME + tableSuffix + System.nanoTime();
+ DynamoDbTable table = enhancedClient.table(currentTestTableName, schema);
+ table.createTable(r -> r.provisionedThroughput(getDefaultProvisionedThroughput()));
+ table.putItem(item);
+ return table;
+ }
+}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/UpdateBehaviorTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/UpdateBehaviorTest.java
index 196d38282277..15342c3f25e5 100644
--- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/UpdateBehaviorTest.java
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/UpdateBehaviorTest.java
@@ -2,9 +2,13 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import com.google.common.collect.ImmutableList;
import java.time.Instant;
import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.After;
@@ -16,6 +20,7 @@
import software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedTimestampRecordExtension;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.CompositeRecord;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.FlattenRecord;
+import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.NestedRecordListElement;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.NestedRecordWithUpdateBehavior;
import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.RecordWithUpdateBehaviors;
import software.amazon.awssdk.enhanced.dynamodb.internal.client.ExtensionResolver;
@@ -62,11 +67,16 @@ public void deleteTable() {
@Test
public void updateBehaviors_firstUpdate() {
- Instant currentTime = Instant.now();
+ Instant currentTime = Instant.now().minusMillis(1);
+ NestedRecordWithUpdateBehavior nestedRecord = new NestedRecordWithUpdateBehavior();
+ nestedRecord.setId("id167");
+ nestedRecord.setNestedUpdateBehaviorAttribute(TEST_BEHAVIOUR_ATTRIBUTE);
+
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
record.setId("id123");
record.setCreatedOn(INSTANT_1);
record.setLastUpdatedOn(INSTANT_2);
+ record.setNestedRecord(nestedRecord);
mappedTable.updateItem(record);
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(record);
@@ -81,28 +91,57 @@ public void updateBehaviors_firstUpdate() {
assertThat(persistedRecord.getLastAutoUpdatedOnMillis().getEpochSecond()).isGreaterThanOrEqualTo(currentTime.getEpochSecond());
assertThat(persistedRecord.getCreatedAutoUpdateOn()).isAfterOrEqualTo(currentTime);
+
+ assertThat(persistedRecord.getNestedRecord().getId()).isEqualTo("id167");
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isAfterOrEqualTo(currentTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isAfterOrEqualTo(currentTime);
+ assertThat(persistedRecord.getCreatedAutoUpdateOn()).isAfterOrEqualTo(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute());
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isEqualTo(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute());
}
@Test
public void updateBehaviors_secondUpdate() {
- Instant beforeUpdateInstant = Instant.now();
+ Instant beforeUpdateInstant = Instant.now().minusMillis(1);
+
+ NestedRecordWithUpdateBehavior secondNestedRecord = new NestedRecordWithUpdateBehavior();
+ secondNestedRecord.setId("id199");
+ secondNestedRecord.setNestedUpdateBehaviorAttribute(TEST_BEHAVIOUR_ATTRIBUTE);
+
+ NestedRecordWithUpdateBehavior nestedRecord = new NestedRecordWithUpdateBehavior();
+ nestedRecord.setId("id155");
+ nestedRecord.setNestedUpdateBehaviorAttribute(TEST_BEHAVIOUR_ATTRIBUTE);
+ nestedRecord.setNestedRecord(secondNestedRecord);
+
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
record.setId("id123");
record.setCreatedOn(INSTANT_1);
record.setLastUpdatedOn(INSTANT_2);
+ record.setNestedRecord(nestedRecord);
mappedTable.updateItem(record);
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(record);
assertThat(persistedRecord.getVersion()).isEqualTo(1L);
+
Instant firstUpdatedTime = persistedRecord.getLastAutoUpdatedOn();
Instant createdAutoUpdateOn = persistedRecord.getCreatedAutoUpdateOn();
+
assertThat(firstUpdatedTime).isAfterOrEqualTo(beforeUpdateInstant);
assertThat(persistedRecord.getFormattedLastAutoUpdatedOn().getEpochSecond())
.isGreaterThanOrEqualTo(beforeUpdateInstant.getEpochSecond());
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isNotNull();
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute())
+ .isEqualTo(firstUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute())
+ .isEqualTo(firstUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedRecord().getNestedCreatedTimeAttribute())
+ .isEqualTo(firstUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedRecord().getNestedUpdatedTimeAttribute())
+ .isEqualTo(firstUpdatedTime);
record.setVersion(1L);
record.setCreatedOn(INSTANT_2);
record.setLastUpdatedOn(INSTANT_2);
+ record.setNestedRecord(nestedRecord);
mappedTable.updateItem(record);
persistedRecord = mappedTable.getItem(record);
@@ -113,6 +152,14 @@ public void updateBehaviors_secondUpdate() {
Instant secondUpdatedTime = persistedRecord.getLastAutoUpdatedOn();
assertThat(secondUpdatedTime).isAfterOrEqualTo(firstUpdatedTime);
assertThat(persistedRecord.getCreatedAutoUpdateOn()).isEqualTo(createdAutoUpdateOn);
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute())
+ .isEqualTo(secondUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute())
+ .isEqualTo(secondUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedRecord().getNestedCreatedTimeAttribute())
+ .isEqualTo(secondUpdatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedRecord().getNestedUpdatedTimeAttribute())
+ .isEqualTo(secondUpdatedTime);
}
@Test
@@ -164,7 +211,7 @@ public void updateBehaviors_transactWriteItems_secondUpdate() {
@Test
public void when_updatingNestedObjectWithSingleLevel_existingInformationIsPreserved_scalar_only_update() {
-
+ Instant currentTime = Instant.now().minusMillis(1);
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
@@ -173,26 +220,35 @@ public void when_updatingNestedObjectWithSingleLevel_existingInformationIsPreser
mappedTable.putItem(record);
+ RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+
+ Instant nestedCreatedTime = persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute();
+ Instant nestedUpdatedTime = persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute();
+ assertThat(nestedCreatedTime).isAfter(currentTime);
+ assertThat(nestedUpdatedTime).isEqualTo(nestedCreatedTime);
+
NestedRecordWithUpdateBehavior updatedNestedRecord = new NestedRecordWithUpdateBehavior();
long updatedNestedCounter = 10L;
updatedNestedRecord.setNestedCounter(updatedNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
- RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+ persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), updatedNestedCounter,
- TEST_BEHAVIOUR_ATTRIBUTE, INSTANT_1);
+ TEST_BEHAVIOUR_ATTRIBUTE, currentTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isEqualTo(nestedCreatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isAfter(nestedUpdatedTime);
}
@Test
public void when_updatingNestedObjectWithSingleLevel_default_mode_update_newMapCreated() {
-
+ Instant currentTime = Instant.now().minusMillis(1);
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
@@ -201,25 +257,34 @@ public void when_updatingNestedObjectWithSingleLevel_default_mode_update_newMapC
mappedTable.putItem(record);
+ RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+
+ Instant nestedCreatedTime = persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute();
+ Instant nestedUpdatedTime = persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute();
+ assertThat(nestedCreatedTime).isNotNull();
+ assertThat(nestedUpdatedTime).isEqualTo(nestedCreatedTime);
+
NestedRecordWithUpdateBehavior updatedNestedRecord = new NestedRecordWithUpdateBehavior();
long updatedNestedCounter = 10L;
updatedNestedRecord.setNestedCounter(updatedNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.DEFAULT));
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.DEFAULT));
- RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+ persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
- verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), updatedNestedCounter, null, null);
+ verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), updatedNestedCounter, null, currentTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isAfter(nestedCreatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isAfter(nestedUpdatedTime);
}
@Test
public void when_updatingNestedObjectWithSingleLevel_with_no_mode_update_newMapCreated() {
-
+ Instant currentTime = Instant.now().minusMillis(1);
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
@@ -232,16 +297,73 @@ public void when_updatingNestedObjectWithSingleLevel_with_no_mode_update_newMapC
long updatedNestedCounter = 10L;
updatedNestedRecord.setNestedCounter(updatedNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
- mappedTable.updateItem(r -> r.item(update_record));
+ mappedTable.updateItem(r -> r.item(updateRecord));
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
- verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), updatedNestedCounter, null, null);
+ verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), updatedNestedCounter, null, currentTime);
+ }
+
+ @Test
+ public void when_updatingNestedObjectList_no_matter_mode_update_newListCreated_with_timestampGenerated() {
+ Instant currentTime = Instant.now().minusMillis(1);
+ NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
+ nestedRecord.setNestedUpdatedTimeAttribute(null);
+ NestedRecordListElement firstElement = new NestedRecordListElement();
+ firstElement.setId("id1");
+ firstElement.setAttribute("attr1");
+ NestedRecordListElement secondElement = new NestedRecordListElement();
+ secondElement.setId("id2");
+ secondElement.setAttribute("attr2");
+ nestedRecord.setNestedRecordList(ImmutableList.of(firstElement, secondElement));
+
+ RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
+ record.setId("id123");
+ record.setNestedRecord(nestedRecord);
+ record.setNestedRecordList(ImmutableList.of(firstElement, secondElement));
+
+ mappedTable.putItem(record);
+
+ RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+
+ List nestedRecordList = persistedRecord.getNestedRecord().getNestedRecordList();
+ Instant firstOperationTime = nestedRecordList.get(0).getTimeAttributeElement();
+
+ assertThat(persistedRecord.getNestedRecordList().get(0).getTimeAttributeElement()).isAfter(currentTime);
+ assertThat(persistedRecord.getNestedRecordList().get(1).getTimeAttributeElement()).isAfter(currentTime);
+ assertThat(nestedRecordList.get(0).getTimeAttributeElement()).isAfter(currentTime);
+ assertThat(nestedRecordList.get(1).getTimeAttributeElement()).isEqualTo(firstOperationTime);
+
+ NestedRecordWithUpdateBehavior updatedNestedRecord = new NestedRecordWithUpdateBehavior();
+ long updatedNestedCounter = 10L;
+ updatedNestedRecord.setNestedUpdatedTimeAttribute(null);
+ firstElement.setAttribute("attr44");
+ secondElement.setAttribute("attr55");
+ updatedNestedRecord.setNestedCounter(updatedNestedCounter);
+ updatedNestedRecord.setNestedRecordList(ImmutableList.of(firstElement, secondElement));
+
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
+ updateRecord.setNestedRecordList(ImmutableList.of(firstElement));
+
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+
+ persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+
+ nestedRecordList = persistedRecord.getNestedRecord().getNestedRecordList();
+
+ assertThat(persistedRecord.getNestedRecordList()).hasSize(1);
+ assertThat(persistedRecord.getNestedRecordList().get(0).getTimeAttributeElement()).isAfter(firstOperationTime);
+ assertThat(nestedRecordList).hasSize(2);
+ assertThat(nestedRecordList.get(0).getTimeAttributeElement()).isAfter(firstOperationTime);
+ assertThat(nestedRecordList.get(1).getTimeAttributeElement()).isAfter(firstOperationTime);
}
@Test
@@ -258,15 +380,59 @@ public void when_updatingNestedObjectToEmptyWithSingleLevel_existingInformationI
NestedRecordWithUpdateBehavior updatedNestedRecord = new NestedRecordWithUpdateBehavior();
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
+
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+
+ RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+ assertThat(persistedRecord.getNestedRecord()).isNotNull();
+ assertThat(persistedRecord.getNestedRecord().getId()).isNull();
+ assertThat(persistedRecord.getNestedRecord().getNestedCounter()).isNull();
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isNull();
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isNotNull();
+ }
+
+ @Test
+ public void when_updatingNestedObjectWithSingleLevel_updateBehaviorIsChecked_scalar_only_update() {
+ Instant currentTime = Instant.now().minusMillis(1);
+ NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
+
+ RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
+ record.setId("id123");
+ record.setNestedRecord(nestedRecord);
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+ mappedTable.putItem(record);
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
- assertThat(persistedRecord.getNestedRecord()).isNull();
+
+ Instant nestedCreatedTime = persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute();
+ Instant nestedUpdatedTime = persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute();
+ assertThat(nestedCreatedTime).isAfter(currentTime);
+ assertThat(nestedUpdatedTime).isEqualTo(nestedCreatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isEqualTo(TEST_BEHAVIOUR_ATTRIBUTE);
+
+ NestedRecordWithUpdateBehavior updatedNestedRecord = new NestedRecordWithUpdateBehavior();
+ long updatedNestedCounter = 10L;
+ updatedNestedRecord.setNestedCounter(updatedNestedCounter);
+ updatedNestedRecord.setNestedUpdateBehaviorAttribute(TEST_BEHAVIOUR_ATTRIBUTE + "updated");
+
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord);
+
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+
+ persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
+
+ //WRITE_IF_NOT_EXISTS detected on createdTimeAttribute and updateBehaviorAttribute -> not changed
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isEqualTo(nestedCreatedTime);
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isEqualTo(TEST_BEHAVIOUR_ATTRIBUTE);
+
+ assertThat(persistedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isAfter(nestedUpdatedTime);
}
private NestedRecordWithUpdateBehavior createNestedWithDefaults(String id, Long counter) {
@@ -274,7 +440,6 @@ private NestedRecordWithUpdateBehavior createNestedWithDefaults(String id, Long
nestedRecordWithDefaults.setId(id);
nestedRecordWithDefaults.setNestedCounter(counter);
nestedRecordWithDefaults.setNestedUpdateBehaviorAttribute(TEST_BEHAVIOUR_ATTRIBUTE);
- nestedRecordWithDefaults.setNestedTimeAttribute(INSTANT_1);
return nestedRecordWithDefaults;
}
@@ -282,31 +447,34 @@ private NestedRecordWithUpdateBehavior createNestedWithDefaults(String id, Long
private void verifyMultipleLevelNestingTargetedUpdateBehavior(NestedRecordWithUpdateBehavior nestedRecord,
long updatedOuterNestedCounter,
long updatedInnerNestedCounter,
- String test_behav_attribute,
- Instant expected_time) {
+ String testBehaviorAttribute,
+ Instant expectedTime) {
assertThat(nestedRecord).isNotNull();
assertThat(nestedRecord.getNestedRecord()).isNotNull();
assertThat(nestedRecord.getNestedCounter()).isEqualTo(updatedOuterNestedCounter);
+ assertThat(nestedRecord.getNestedCreatedTimeAttribute()).isAfter(expectedTime);
+ assertThat(nestedRecord.getNestedUpdatedTimeAttribute()).isAfter(expectedTime);
assertThat(nestedRecord.getNestedRecord()).isNotNull();
assertThat(nestedRecord.getNestedRecord().getNestedCounter()).isEqualTo(updatedInnerNestedCounter);
assertThat(nestedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isEqualTo(
- test_behav_attribute);
- assertThat(nestedRecord.getNestedRecord().getNestedTimeAttribute()).isEqualTo(expected_time);
+ testBehaviorAttribute);
+ assertThat(nestedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isAfter(expectedTime);
+ assertThat(nestedRecord.getNestedRecord().getNestedUpdatedTimeAttribute()).isAfter(expectedTime);
}
private void verifySingleLevelNestingTargetedUpdateBehavior(NestedRecordWithUpdateBehavior nestedRecord,
- long updatedNestedCounter, String expected_behav_attr,
- Instant expected_time) {
+ long updatedNestedCounter, String expectedBehaviorAttr,
+ Instant expectedTime) {
assertThat(nestedRecord).isNotNull();
assertThat(nestedRecord.getNestedCounter()).isEqualTo(updatedNestedCounter);
- assertThat(nestedRecord.getNestedUpdateBehaviorAttribute()).isEqualTo(expected_behav_attr);
- assertThat(nestedRecord.getNestedTimeAttribute()).isEqualTo(expected_time);
+ assertThat(nestedRecord.getNestedUpdateBehaviorAttribute()).isEqualTo(expectedBehaviorAttr);
+ assertThat(nestedRecord.getNestedCreatedTimeAttribute()).isAfter(expectedTime);
+ assertThat(nestedRecord.getNestedUpdatedTimeAttribute()).isAfter(expectedTime);
}
@Test
public void when_updatingNestedObjectWithMultipleLevels_inScalarOnlyMode_existingInformationIsPreserved() {
-
NestedRecordWithUpdateBehavior nestedRecord1 = createNestedWithDefaults("id789", 50L);
NestedRecordWithUpdateBehavior nestedRecord2 = createNestedWithDefaults("id456", 0L);
@@ -327,12 +495,12 @@ public void when_updatingNestedObjectWithMultipleLevels_inScalarOnlyMode_existin
long outerNestedCounter = 200L;
updatedNestedRecord1.setNestedCounter(outerNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord1);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord1);
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
@@ -342,7 +510,6 @@ public void when_updatingNestedObjectWithMultipleLevels_inScalarOnlyMode_existin
@Test
public void when_updatingNestedObjectWithMultipleLevels_inMapsOnlyMode_existingInformationIsPreserved() {
-
NestedRecordWithUpdateBehavior nestedRecord1 = createNestedWithDefaults("id789", 50L);
NestedRecordWithUpdateBehavior nestedRecord2 = createNestedWithDefaults("id456", 0L);
@@ -358,12 +525,12 @@ public void when_updatingNestedObjectWithMultipleLevels_inMapsOnlyMode_existingI
long outerNestedCounter = 200L;
updatedNestedRecord1.setNestedCounter(outerNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord1);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord1);
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY));
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY));
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
@@ -373,7 +540,7 @@ public void when_updatingNestedObjectWithMultipleLevels_inMapsOnlyMode_existingI
@Test
public void when_updatingNestedObjectWithMultipleLevels_default_mode_existingInformationIsErased() {
-
+ Instant currentTime = Instant.now().minusMillis(1);
NestedRecordWithUpdateBehavior nestedRecord1 = createNestedWithDefaults("id789", 50L);
NestedRecordWithUpdateBehavior nestedRecord2 = createNestedWithDefaults("id456", 0L);
@@ -394,22 +561,21 @@ public void when_updatingNestedObjectWithMultipleLevels_default_mode_existingInf
long outerNestedCounter = 200L;
updatedNestedRecord1.setNestedCounter(outerNestedCounter);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setNestedRecord(updatedNestedRecord1);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setNestedRecord(updatedNestedRecord1);
- mappedTable.updateItem(r -> r.item(update_record));
+ mappedTable.updateItem(r -> r.item(updateRecord));
RecordWithUpdateBehaviors persistedRecord = mappedTable.getItem(r -> r.key(k -> k.partitionValue("id123")));
verifyMultipleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), outerNestedCounter, innerNestedCounter, null,
- null);
+ currentTime);
}
@Test
public void when_updatingNestedNonScalarObject_scalar_only_update_throwsDynamoDBException() {
-
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
nestedRecord.setAttribute(TEST_ATTRIBUTE);
@@ -418,35 +584,34 @@ public void when_updatingNestedNonScalarObject_scalar_only_update_throwsDynamoDB
mappedTable.putItem(record);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setKey("abc");
- update_record.setNestedRecord(nestedRecord);
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setKey("abc");
+ updateRecord.setNestedRecord(nestedRecord);
- assertThatThrownBy(() -> mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)))
+ assertThatThrownBy(() -> mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)))
.isInstanceOf(DynamoDbException.class);
}
@Test
public void when_updatingNestedMap_mapsOnlyMode_newMapIsCreatedAndStored() {
-
RecordWithUpdateBehaviors record = new RecordWithUpdateBehaviors();
record.setId("id123");
mappedTable.putItem(record);
- RecordWithUpdateBehaviors update_record = new RecordWithUpdateBehaviors();
- update_record.setId("id123");
- update_record.setVersion(1L);
- update_record.setKey("abc");
+ RecordWithUpdateBehaviors updateRecord = new RecordWithUpdateBehaviors();
+ updateRecord.setId("id123");
+ updateRecord.setVersion(1L);
+ updateRecord.setKey("abc");
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id456", 5L);
nestedRecord.setAttribute(TEST_ATTRIBUTE);
- update_record.setNestedRecord(nestedRecord);
+ updateRecord.setNestedRecord(nestedRecord);
RecordWithUpdateBehaviors persistedRecord =
- mappedTable.updateItem(r -> r.item(update_record).ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY));
+ mappedTable.updateItem(r -> r.item(updateRecord).ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY));
verifySingleLevelNestingTargetedUpdateBehavior(persistedRecord.getNestedRecord(), 5L, TEST_BEHAVIOUR_ATTRIBUTE,
INSTANT_1);
@@ -470,21 +635,20 @@ public void when_emptyNestedRecordIsSet_emptyMapIsStoredInTable() {
.build());
assertThat(getItemResponse.item().get("nestedRecord")).isNotNull();
- assertThat(getItemResponse.item().get("nestedRecord").toString()).isEqualTo("AttributeValue(M={nestedTimeAttribute"
- + "=AttributeValue(NUL=true), "
- + "nestedRecord=AttributeValue(NUL=true), "
- + "attribute=AttributeValue(NUL=true), "
- + "id=AttributeValue(NUL=true), "
- + "nestedUpdateBehaviorAttribute=AttributeValue"
- + "(NUL=true), nestedCounter=AttributeValue"
- + "(NUL=true), nestedVersionedAttribute"
- + "=AttributeValue(NUL=true)})");
+ Map nestedRecord = getItemResponse.item().get("nestedRecord").m();
+ assertThat(nestedRecord.get("nestedCreatedTimeAttribute")).isNotNull();
+ assertThat(nestedRecord.get("nestedUpdatedTimeAttribute")).isNotNull();
+ assertTrue(nestedRecord.get("id").nul());
+ assertTrue(nestedRecord.get("nestedRecord").nul());
+ assertTrue(nestedRecord.get("attribute").nul());
+ assertTrue(nestedRecord.get("nestedUpdateBehaviorAttribute").nul());
+ assertTrue(nestedRecord.get("nestedCounter").nul());
+ assertTrue(nestedRecord.get("nestedVersionedAttribute").nul());
}
@Test
public void when_updatingNestedObjectWithSingleLevelFlattened_existingInformationIsPreserved_scalar_only_update() {
-
NestedRecordWithUpdateBehavior nestedRecord = createNestedWithDefaults("id123", 10L);
CompositeRecord compositeRecord = new CompositeRecord();
@@ -513,12 +677,9 @@ public void when_updatingNestedObjectWithSingleLevelFlattened_existingInformatio
verifySingleLevelNestingTargetedUpdateBehavior(persistedFlattenedRecord.getCompositeRecord().getNestedRecord(), 100L,
TEST_BEHAVIOUR_ATTRIBUTE, INSTANT_1);
}
-
-
@Test
public void when_updatingNestedObjectWithMultipleLevelFlattened_existingInformationIsPreserved_scalar_only_update() {
-
NestedRecordWithUpdateBehavior outerNestedRecord = createNestedWithDefaults("id123", 10L);
NestedRecordWithUpdateBehavior innerNestedRecord = createNestedWithDefaults("id456", 5L);
outerNestedRecord.setNestedRecord(innerNestedRecord);
@@ -555,10 +716,11 @@ public void when_updatingNestedObjectWithMultipleLevelFlattened_existingInformat
50L, TEST_BEHAVIOUR_ATTRIBUTE, INSTANT_1);
assertThat(persistedFlattenedRecord.getCompositeRecord().getNestedRecord().getNestedCounter()).isEqualTo(100L);
assertThat(persistedFlattenedRecord.getCompositeRecord().getNestedRecord().getNestedRecord().getNestedCounter()).isEqualTo(50L);
+ assertThat(persistedFlattenedRecord.getCompositeRecord().getNestedRecord().getNestedRecord().getNestedCreatedTimeAttribute()).isNotNull();
}
/**
- * Currently, nested records are not updated through extensions.
+ * Currently, nested records are not updated through extensions (only the timestamp).
*/
@Test
public void updateBehaviors_nested() {
@@ -579,6 +741,6 @@ public void updateBehaviors_nested() {
assertThat(persistedRecord.getNestedRecord().getNestedVersionedAttribute()).isNull();
assertThat(persistedRecord.getNestedRecord().getNestedCounter()).isNull();
assertThat(persistedRecord.getNestedRecord().getNestedUpdateBehaviorAttribute()).isNull();
- assertThat(persistedRecord.getNestedRecord().getNestedTimeAttribute()).isNull();
+ assertThat(persistedRecord.getNestedRecord().getNestedCreatedTimeAttribute()).isNotNull();
}
}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampModels.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampModels.java
new file mode 100644
index 000000000000..fb8eb5b4837b
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/AutogeneratedTimestampModels.java
@@ -0,0 +1,3602 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.functionaltests.models;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static software.amazon.awssdk.enhanced.dynamodb.extensions.AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute;
+import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
+import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedTimestampAttribute;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.ImmutableTableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.StaticTableSchema;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
+import software.amazon.awssdk.utils.ImmutableMap;
+
+public final class AutogeneratedTimestampModels {
+
+ private AutogeneratedTimestampModels() {
+ }
+
+ // Constants
+ public static final String ID_1 = "1";
+ public static final String ID_2 = "2";
+ public static final String ID_ATTR = "id";
+ public static final String TIME_ATTR = "time";
+
+ public static final String ATTR_LEVEL1 = "attr_level1";
+ public static final String ATTR_LEVEL2 = "attr_level2";
+ public static final String ATTR_LEVEL3 = "attr_level3";
+ public static final String ATTR_LEVEL4 = "attr_level4";
+ public static final String ATTR_CHILD1 = "attr_child1";
+ public static final String ATTR_CHILD2 = "attr_child2";
+
+ public static final String CHILD1_KEY = "child1";
+ public static final String CHILD2_KEY = "child2";
+ public static final String LEVEL2_KEY = "level2";
+ public static final String LEVEL3_KEY = "level3";
+ public static final String LEVEL4_KEY = "level4";
+
+
+ // Simple Bean Records
+ @DynamoDbBean
+ public static class SimpleBeanRecordWithList {
+ private String id;
+ private String attr;
+ private Instant time;
+ private List childList;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public SimpleBeanRecordWithList setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleBeanRecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleBeanRecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+
+ public List getChildList() {
+ return childList == null ? null : Collections.unmodifiableList(childList);
+ }
+
+ public SimpleBeanRecordWithList setChildList(List childList) {
+ this.childList = childList;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class SimpleBeanRecordWithSet {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Set childSet;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public SimpleBeanRecordWithSet setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleBeanRecordWithSet setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleBeanRecordWithSet setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Set getChildSet() {
+ return childSet == null ? null : Collections.unmodifiableSet(childSet);
+ }
+
+ public SimpleBeanRecordWithSet setChildSet(Set childSet) {
+ this.childSet = childSet;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class SimpleBeanRecordWithMap {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Map childMap;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public SimpleBeanRecordWithMap setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleBeanRecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleBeanRecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Map getChildMap() {
+ return childMap == null ? null : Collections.unmodifiableMap(childMap);
+ }
+
+ public SimpleBeanRecordWithMap setChildMap(Map childMap) {
+ this.childMap = childMap;
+ return this;
+ }
+ }
+
+ // simple record used by list/set/map as the deepest nested level
+ @DynamoDbBean
+ public static class SimpleBeanChild {
+ private String id;
+ private String attr;
+ private Instant time;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public SimpleBeanChild setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleBeanChild setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleBeanChild setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+ }
+
+
+ // Nested Bean Records
+ @DynamoDbBean
+ public static class NestedBeanRecordWithList {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel2RecordWithList level2;
+ private List level2List;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public NestedBeanRecordWithList setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanRecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanRecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel2RecordWithList getLevel2() {
+ return level2;
+ }
+
+ public NestedBeanRecordWithList setLevel2(NestedBeanLevel2RecordWithList level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public List getLevel2List() {
+ return level2List == null ? null : Collections.unmodifiableList(level2List);
+ }
+
+ public NestedBeanRecordWithList setLevel2List(List level2List) {
+ this.level2List = level2List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedBeanRecordWithList)) {
+ return false;
+ }
+
+ NestedBeanRecordWithList that = (NestedBeanRecordWithList) o;
+ return Objects.equals(id, that.id) &&
+ Objects.equals(attr, that.attr) &&
+ Objects.equals(time, that.time) &&
+ Objects.equals(level2, that.level2) &&
+ Objects.equals(level2List, that.level2List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(id);
+ result = 31 * result + Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level2);
+ result = 31 * result + Objects.hashCode(level2List);
+ return result;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel2RecordWithList {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel3RecordWithList level3;
+ private List level3List;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel2RecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel2RecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel3RecordWithList getLevel3() {
+ return level3;
+ }
+
+ public NestedBeanLevel2RecordWithList setLevel3(NestedBeanLevel3RecordWithList level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public List getLevel3List() {
+ return level3List;
+ }
+
+ public NestedBeanLevel2RecordWithList setLevel3List(List level3List) {
+ this.level3List = level3List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedBeanLevel2RecordWithList)) {
+ return false;
+ }
+
+ NestedBeanLevel2RecordWithList that = (NestedBeanLevel2RecordWithList) o;
+ return Objects.equals(attr, that.attr) &&
+ Objects.equals(time, that.time) &&
+ Objects.equals(level3, that.level3) &&
+ Objects.equals(level3List, that.level3List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level3);
+ result = 31 * result + Objects.hashCode(level3List);
+ return result;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel3RecordWithList {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel4Record level4;
+ private List level4List;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel3RecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel3RecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedBeanLevel3RecordWithList setLevel4(NestedBeanLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public List getLevel4List() {
+ return level4List;
+ }
+
+ public NestedBeanLevel3RecordWithList setLevel4List(List level4List) {
+ this.level4List = level4List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedBeanLevel3RecordWithList)) {
+ return false;
+ }
+
+ NestedBeanLevel3RecordWithList that = (NestedBeanLevel3RecordWithList) o;
+ return Objects.equals(attr, that.attr) &&
+ Objects.equals(time, that.time) &&
+ Objects.equals(level4, that.level4) &&
+ Objects.equals(level4List, that.level4List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level4);
+ result = 31 * result + Objects.hashCode(level4List);
+ return result;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanRecordWithSet {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel2RecordWithSet level2;
+ private Set level2Set;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public NestedBeanRecordWithSet setId(String v) {
+ id = v;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanRecordWithSet setAttr(String v) {
+ attr = v;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanRecordWithSet setTime(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public NestedBeanLevel2RecordWithSet getLevel2() {
+ return level2;
+ }
+
+ public NestedBeanRecordWithSet setLevel2(NestedBeanLevel2RecordWithSet v) {
+ level2 = v;
+ return this;
+ }
+
+ public Set getLevel2Set() {
+ return level2Set;
+ }
+
+ public NestedBeanRecordWithSet setLevel2Set(Set v) {
+ level2Set = v;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel2RecordWithSet {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel3RecordWithSet level3;
+ private Set level3Set;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel2RecordWithSet setAttr(String v) {
+ attr = v;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel2RecordWithSet setTime(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public NestedBeanLevel3RecordWithSet getLevel3() {
+ return level3;
+ }
+
+ public NestedBeanLevel2RecordWithSet setLevel3(NestedBeanLevel3RecordWithSet v) {
+ level3 = v;
+ return this;
+ }
+
+ public Set getLevel3Set() {
+ return level3Set;
+ }
+
+ public NestedBeanLevel2RecordWithSet setLevel3Set(Set v) {
+ level3Set = v;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel3RecordWithSet {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel4Record level4;
+ private Set level4Set;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel3RecordWithSet setAttr(String v) {
+ attr = v;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel3RecordWithSet setTime(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public NestedBeanLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedBeanLevel3RecordWithSet setLevel4(NestedBeanLevel4Record v) {
+ level4 = v;
+ return this;
+ }
+
+ public Set getLevel4Set() {
+ return level4Set;
+ }
+
+ public NestedBeanLevel3RecordWithSet setLevel4Set(Set v) {
+ level4Set = v;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanRecordWithMap {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel2RecordWithMap level2;
+ private Map level2Map;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public NestedBeanRecordWithMap setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanRecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanRecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel2RecordWithMap getLevel2() {
+ return level2;
+ }
+
+ public NestedBeanRecordWithMap setLevel2(NestedBeanLevel2RecordWithMap level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public Map getLevel2Map() {
+ return Collections.unmodifiableMap(level2Map);
+ }
+
+ public NestedBeanRecordWithMap setLevel2Map(Map level2Map) {
+ this.level2Map = level2Map;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel2RecordWithMap {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel3RecordWithMap level3;
+ private Map level3Map;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel2RecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel2RecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel3RecordWithMap getLevel3() {
+ return level3;
+ }
+
+ public NestedBeanLevel2RecordWithMap setLevel3(NestedBeanLevel3RecordWithMap level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public Map getLevel3Map() {
+ return Collections.unmodifiableMap(level3Map);
+ }
+
+ public NestedBeanLevel2RecordWithMap setLevel3Map(Map level3Map) {
+ this.level3Map = level3Map;
+ return this;
+ }
+ }
+
+ @DynamoDbBean
+ public static class NestedBeanLevel3RecordWithMap {
+ private String attr;
+ private Instant time;
+ private NestedBeanLevel4Record level4;
+ private Map level4Map;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel3RecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel3RecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedBeanLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedBeanLevel3RecordWithMap setLevel4(NestedBeanLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public Map getLevel4Map() {
+ return Collections.unmodifiableMap(level4Map);
+ }
+
+ public NestedBeanLevel3RecordWithMap setLevel4Map(Map level4Map) {
+ this.level4Map = level4Map;
+ return this;
+ }
+ }
+
+ // nested record used by list/set/map as the deepest nested level
+ @DynamoDbBean
+ public static class NestedBeanLevel4Record {
+ private String id;
+ private String attr;
+ private Instant time;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public NestedBeanLevel4Record setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedBeanLevel4Record setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedBeanLevel4Record setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedBeanLevel4Record)) {
+ return false;
+ }
+
+ NestedBeanLevel4Record that = (NestedBeanLevel4Record) o;
+ return Objects.equals(attr, that.attr) &&
+ Objects.equals(time, that.time);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ return result;
+ }
+ }
+
+
+ // Simple Immutable Records
+ @DynamoDbImmutable(builder = SimpleImmutableRecordWithList.Builder.class)
+ public static final class SimpleImmutableRecordWithList {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final List childList;
+
+ private SimpleImmutableRecordWithList(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ this.childList = b.childList;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+
+ public List getChildList() {
+ return Collections.unmodifiableList(childList);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private List childList;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder childList(List childList) {
+ this.childList = childList;
+ return this;
+ }
+
+ public SimpleImmutableRecordWithList build() {
+ return new SimpleImmutableRecordWithList(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = SimpleImmutableRecordWithSet.Builder.class)
+ public static class SimpleImmutableRecordWithSet {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final Set childSet;
+
+ private SimpleImmutableRecordWithSet(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ this.childSet = b.childSet;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+
+ public Set getChildSet() {
+ return Collections.unmodifiableSet(childSet);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Set childSet;
+
+ public Builder id(String v) {
+ this.id = v;
+ return this;
+ }
+
+ public Builder attr(String v) {
+ this.attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ this.time = v;
+ return this;
+ }
+
+ public Builder childSet(Set v) {
+ this.childSet = v;
+ return this;
+ }
+
+ public SimpleImmutableRecordWithSet build() {
+ return new SimpleImmutableRecordWithSet(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = SimpleImmutableRecordWithMap.Builder.class)
+ public static class SimpleImmutableRecordWithMap {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final Map childMap;
+
+ private SimpleImmutableRecordWithMap(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ this.childMap = b.childMap;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public Map getChildMap() {
+ return Collections.unmodifiableMap(childMap);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Map childMap;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder childMap(Map childMap) {
+ this.childMap = childMap;
+ return this;
+ }
+
+ public SimpleImmutableRecordWithMap build() {
+ return new SimpleImmutableRecordWithMap(this);
+ }
+ }
+ }
+
+ // simple record used by list/set/map as the deepest nested level
+ @DynamoDbImmutable(builder = SimpleImmutableChild.Builder.class)
+ public static final class SimpleImmutableChild {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+
+ private SimpleImmutableChild(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public SimpleImmutableChild build() {
+ return new SimpleImmutableChild(this);
+ }
+ }
+ }
+
+
+ // Nested Immutable Records
+ @DynamoDbImmutable(builder = NestedImmutableRecordWithList.Builder.class)
+ public static final class NestedImmutableRecordWithList {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel2RecordWithList level2;
+ private final List level2List;
+
+ private NestedImmutableRecordWithList(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level2 = b.level2;
+ this.level2List = b.level2List;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel2RecordWithList getLevel2() {
+ return level2;
+ }
+
+ public List getLevel2List() {
+ return level2List == null ? null : Collections.unmodifiableList(level2List);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel2RecordWithList level2;
+ private List level2List;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder level2(NestedImmutableLevel2RecordWithList level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public Builder level2List(List level2List) {
+ this.level2List = level2List;
+ return this;
+ }
+
+ public NestedImmutableRecordWithList build() {
+ return new NestedImmutableRecordWithList(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel2RecordWithList.Builder.class)
+ public static final class NestedImmutableLevel2RecordWithList {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel3RecordWithList level3;
+ private final List level3List;
+
+ private NestedImmutableLevel2RecordWithList(Builder b) {
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level3 = b.level3;
+ this.level3List = b.level3List;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel3RecordWithList getLevel3() {
+ return level3;
+ }
+
+ public List getLevel3List() {
+ return level3List;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel3RecordWithList level3;
+ private List level3List;
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder level3(NestedImmutableLevel3RecordWithList level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public Builder level3List(List level3List) {
+ this.level3List = level3List;
+ return this;
+ }
+
+ public NestedImmutableLevel2RecordWithList build() {
+ return new NestedImmutableLevel2RecordWithList(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel3RecordWithList.Builder.class)
+ public static final class NestedImmutableLevel3RecordWithList {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel4Record level4;
+ private final List level4List;
+
+ private NestedImmutableLevel3RecordWithList(Builder b) {
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level4 = b.level4;
+ this.level4List = b.level4List;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public List getLevel4List() {
+ return level4List;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel4Record level4;
+ private List level4List;
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder level4(NestedImmutableLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public Builder level4List(List level4List) {
+ this.level4List = level4List;
+ return this;
+ }
+
+ public NestedImmutableLevel3RecordWithList build() {
+ return new NestedImmutableLevel3RecordWithList(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableRecordWithSet.Builder.class)
+ public static final class NestedImmutableRecordWithSet {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel2RecordWithSet level2;
+ private final Set level2Set;
+
+ private NestedImmutableRecordWithSet(Builder b) {
+ id = b.id;
+ attr = b.attr;
+ time = b.time;
+ level2 = b.level2;
+ level2Set = b.level2Set;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel2RecordWithSet getLevel2() {
+ return level2;
+ }
+
+ public Set getLevel2Set() {
+ return level2Set == null ? null : Collections.unmodifiableSet(level2Set);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel2RecordWithSet level2;
+ private Set level2Set;
+
+ public Builder id(String v) {
+ id = v;
+ return this;
+ }
+
+ public Builder attr(String v) {
+ attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public Builder level2(NestedImmutableLevel2RecordWithSet v) {
+ level2 = v;
+ return this;
+ }
+
+ public Builder level2Set(Set v) {
+ level2Set = v;
+ return this;
+ }
+
+ public NestedImmutableRecordWithSet build() {
+ return new NestedImmutableRecordWithSet(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel2RecordWithSet.Builder.class)
+ public static class NestedImmutableLevel2RecordWithSet {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel3RecordWithSet level3;
+ private final Set level3Set;
+
+ private NestedImmutableLevel2RecordWithSet(Builder b) {
+ attr = b.attr;
+ time = b.time;
+ level3 = b.level3;
+ level3Set = b.level3Set;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel3RecordWithSet getLevel3() {
+ return level3;
+ }
+
+ public Set getLevel3Set() {
+ return level3Set == null ? null : Collections.unmodifiableSet(level3Set);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel3RecordWithSet level3;
+ private Set level3Set;
+
+ public Builder attr(String v) {
+ attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public Builder level3(NestedImmutableLevel3RecordWithSet v) {
+ level3 = v;
+ return this;
+ }
+
+ public Builder level3Set(Set v) {
+ level3Set = v;
+ return this;
+ }
+
+ public NestedImmutableLevel2RecordWithSet build() {
+ return new NestedImmutableLevel2RecordWithSet(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel3RecordWithSet.Builder.class)
+ public static class NestedImmutableLevel3RecordWithSet {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel4Record level4;
+ private final Set level4Set;
+
+ private NestedImmutableLevel3RecordWithSet(Builder b) {
+ attr = b.attr;
+ time = b.time;
+ level4 = b.level4;
+ level4Set = b.level4Set;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public Set getLevel4Set() {
+ return level4Set == null ? null : Collections.unmodifiableSet(level4Set);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel4Record level4;
+ private Set level4Set;
+
+ public Builder attr(String v) {
+ attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ time = v;
+ return this;
+ }
+
+ public Builder level4(NestedImmutableLevel4Record v) {
+ level4 = v;
+ return this;
+ }
+
+ public Builder level4Set(Set v) {
+ level4Set = v;
+ return this;
+ }
+
+ public NestedImmutableLevel3RecordWithSet build() {
+ return new NestedImmutableLevel3RecordWithSet(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableRecordWithMap.Builder.class)
+ public static class NestedImmutableRecordWithMap {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel2RecordWithMap level2;
+ private final Map level2Map;
+
+ private NestedImmutableRecordWithMap(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level2 = b.level2;
+ this.level2Map = b.level2Map;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel2RecordWithMap getLevel2() {
+ return level2;
+ }
+
+ public Map getLevel2Map() {
+ return level2Map == null ? null : Collections.unmodifiableMap(level2Map);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel2RecordWithMap level2;
+ private Map level2Map;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Builder level2(NestedImmutableLevel2RecordWithMap v) {
+ this.level2 = v;
+ return this;
+ }
+
+ public Builder level2Map(Map v) {
+ this.level2Map = v;
+ return this;
+ }
+
+ public NestedImmutableRecordWithMap build() {
+ return new NestedImmutableRecordWithMap(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel2RecordWithMap.Builder.class)
+ public static class NestedImmutableLevel2RecordWithMap {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel3RecordWithMap level3;
+ private final Map level3Map;
+
+ private NestedImmutableLevel2RecordWithMap(Builder b) {
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level3 = b.level3;
+ this.level3Map = b.level3Map;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel3RecordWithMap getLevel3() {
+ return level3;
+ }
+
+ public Map getLevel3Map() {
+ return level3Map == null ? null : Collections.unmodifiableMap(level3Map);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel3RecordWithMap level3;
+ private Map level3Map;
+
+ public Builder attr(String v) {
+ this.attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ this.time = v;
+ return this;
+ }
+
+ public Builder level3(NestedImmutableLevel3RecordWithMap v) {
+ this.level3 = v;
+ return this;
+ }
+
+ public Builder level3Map(Map v) {
+ this.level3Map = v;
+ return this;
+ }
+
+ public NestedImmutableLevel2RecordWithMap build() {
+ return new NestedImmutableLevel2RecordWithMap(this);
+ }
+ }
+ }
+
+ @DynamoDbImmutable(builder = NestedImmutableLevel3RecordWithMap.Builder.class)
+ public static class NestedImmutableLevel3RecordWithMap {
+ private final String attr;
+ private final Instant time;
+ private final NestedImmutableLevel4Record level4;
+ private final Map level4Map;
+
+ private NestedImmutableLevel3RecordWithMap(Builder b) {
+ this.attr = b.attr;
+ this.time = b.time;
+ this.level4 = b.level4;
+ this.level4Map = b.level4Map;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedImmutableLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public Map getLevel4Map() {
+ return level4Map == null ? null : Collections.unmodifiableMap(level4Map);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String attr;
+ private Instant time;
+ private NestedImmutableLevel4Record level4;
+ private Map level4Map;
+
+ public Builder attr(String v) {
+ this.attr = v;
+ return this;
+ }
+
+ public Builder time(Instant v) {
+ this.time = v;
+ return this;
+ }
+
+ public Builder level4(NestedImmutableLevel4Record v) {
+ this.level4 = v;
+ return this;
+ }
+
+ public Builder level4Map(Map v) {
+ this.level4Map = v;
+ return this;
+ }
+
+ public NestedImmutableLevel3RecordWithMap build() {
+ return new NestedImmutableLevel3RecordWithMap(this);
+ }
+ }
+ }
+
+ // nested record used by list/set/map as the deepest nested level
+ @DynamoDbImmutable(builder = NestedImmutableLevel4Record.Builder.class)
+ public static final class NestedImmutableLevel4Record {
+ private final String id;
+ private final String attr;
+ private final Instant time;
+
+ private NestedImmutableLevel4Record(Builder b) {
+ this.id = b.id;
+ this.attr = b.attr;
+ this.time = b.time;
+ }
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTime() {
+ return time;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String id;
+ private String attr;
+ private Instant time;
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder attr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Builder time(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedImmutableLevel4Record build() {
+ return new NestedImmutableLevel4Record(this);
+ }
+ }
+ }
+
+
+ // Simple Static Records
+ public static class SimpleStaticRecordWithSet {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Set childSet;
+
+ public String getId() {
+ return id;
+ }
+
+ public SimpleStaticRecordWithSet setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleStaticRecordWithSet setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleStaticRecordWithSet setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Set getChildSet() {
+ return Collections.unmodifiableSet(childSet);
+ }
+
+ public SimpleStaticRecordWithSet setChildSet(Set childSet) {
+ this.childSet = childSet;
+ return this;
+ }
+ }
+
+ public static class SimpleStaticRecordWithMap {
+ private String id;
+ private String attr;
+ private Instant time;
+ private Map childMap;
+
+ public String getId() {
+ return id;
+ }
+
+ public SimpleStaticRecordWithMap setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleStaticRecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleStaticRecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public Map getChildMap() {
+ return Collections.unmodifiableMap(childMap);
+ }
+
+ public SimpleStaticRecordWithMap setChildMap(Map childMap) {
+ this.childMap = childMap;
+ return this;
+ }
+ }
+
+ public static class SimpleStaticRecordWithList {
+ private String id;
+ private String attr;
+ private Instant time;
+ private List childList;
+
+ public String getId() {
+ return id;
+ }
+
+ public SimpleStaticRecordWithList setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleStaticRecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleStaticRecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+
+ public List getChildList() {
+ return childList == null ? null : Collections.unmodifiableList(childList);
+ }
+
+ public SimpleStaticRecordWithList setChildList(List childList) {
+ this.childList = childList;
+ return this;
+ }
+ }
+
+ // simple record used by list/set/map as the deepest nested level
+ public static class SimpleStaticChild {
+ private String id;
+ private String attr;
+ private Instant time;
+
+ public String getId() {
+ return id;
+ }
+
+ public SimpleStaticChild setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public SimpleStaticChild setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public SimpleStaticChild setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+ }
+
+
+ // Nested Static Records
+ public static class NestedStaticRecordWithList {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel2RecordWithList level2;
+ private List level2List;
+
+ public String getId() {
+ return id;
+ }
+
+ public NestedStaticRecordWithList setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticRecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticRecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel2RecordWithList getLevel2() {
+ return level2;
+ }
+
+ public NestedStaticRecordWithList setLevel2(NestedStaticLevel2RecordWithList level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public List getLevel2List() {
+ return level2List;
+ }
+
+ public NestedStaticRecordWithList setLevel2List(List level2List) {
+ this.level2List = level2List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticRecordWithList)) {
+ return false;
+ }
+
+ NestedStaticRecordWithList that = (NestedStaticRecordWithList) o;
+ return Objects.equals(id, that.id) && Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level2, that.level2) && Objects.equals(level2List, that.level2List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(id);
+ result = 31 * result + Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level2);
+ result = 31 * result + Objects.hashCode(level2List);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel2RecordWithList {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel3RecordWithList level3;
+ private List level3List;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel2RecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel2RecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel3RecordWithList getLevel3() {
+ return level3;
+ }
+
+ public NestedStaticLevel2RecordWithList setLevel3(NestedStaticLevel3RecordWithList level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public List getLevel3List() {
+ return level3List;
+ }
+
+ public NestedStaticLevel2RecordWithList setLevel3List(List level3List) {
+ this.level3List = level3List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel2RecordWithList)) {
+ return false;
+ }
+
+ NestedStaticLevel2RecordWithList that = (NestedStaticLevel2RecordWithList) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level3, that.level3) && Objects.equals(level3List, that.level3List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level3);
+ result = 31 * result + Objects.hashCode(level3List);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel3RecordWithList {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel4Record level4;
+ private List level4List;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel3RecordWithList setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel3RecordWithList setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedStaticLevel3RecordWithList setLevel4(NestedStaticLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public List getLevel4List() {
+ return level4List;
+ }
+
+ public NestedStaticLevel3RecordWithList setLevel4List(List level4List) {
+ this.level4List = level4List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel3RecordWithList)) {
+ return false;
+ }
+
+ NestedStaticLevel3RecordWithList that = (NestedStaticLevel3RecordWithList) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level4, that.level4) && Objects.equals(level4List, that.level4List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level4);
+ result = 31 * result + Objects.hashCode(level4List);
+ return result;
+ }
+ }
+
+ public static class NestedStaticRecordWithSet {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel2RecordWithSet level2;
+ private Set level2List;
+
+ public String getId() {
+ return id;
+ }
+
+ public NestedStaticRecordWithSet setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticRecordWithSet setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticRecordWithSet setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel2RecordWithSet getLevel2() {
+ return level2;
+ }
+
+ public NestedStaticRecordWithSet setLevel2(NestedStaticLevel2RecordWithSet level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public Set getLevel2List() {
+ return level2List;
+ }
+
+ public NestedStaticRecordWithSet setLevel2List(Set level2List) {
+ this.level2List = level2List;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticRecordWithSet)) {
+ return false;
+ }
+
+ NestedStaticRecordWithSet that = (NestedStaticRecordWithSet) o;
+ return Objects.equals(id, that.id) && Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level2, that.level2) && Objects.equals(level2List, that.level2List);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(id);
+ result = 31 * result + Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level2);
+ result = 31 * result + Objects.hashCode(level2List);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel2RecordWithSet {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel3RecordWithSet level3;
+ private Set level3Set;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel2RecordWithSet setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel2RecordWithSet setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel3RecordWithSet getLevel3() {
+ return level3;
+ }
+
+ public NestedStaticLevel2RecordWithSet setLevel3(NestedStaticLevel3RecordWithSet level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public Set getLevel3Set() {
+ return level3Set;
+ }
+
+ public NestedStaticLevel2RecordWithSet setLevel3Set(Set level3Set) {
+ this.level3Set = level3Set;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel2RecordWithSet)) {
+ return false;
+ }
+
+ NestedStaticLevel2RecordWithSet that = (NestedStaticLevel2RecordWithSet) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level3, that.level3) && Objects.equals(level3Set, that.level3Set);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level3);
+ result = 31 * result + Objects.hashCode(level3Set);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel3RecordWithSet {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel4Record level4;
+ private Set level4Set;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel3RecordWithSet setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel3RecordWithSet setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedStaticLevel3RecordWithSet setLevel4(NestedStaticLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public Set getLevel4Set() {
+ return level4Set;
+ }
+
+ public NestedStaticLevel3RecordWithSet setLevel4Set(Set level4Set) {
+ this.level4Set = level4Set;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel3RecordWithSet)) {
+ return false;
+ }
+
+ NestedStaticLevel3RecordWithSet that = (NestedStaticLevel3RecordWithSet) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level4, that.level4) && Objects.equals(level4Set, that.level4Set);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level4);
+ result = 31 * result + Objects.hashCode(level4Set);
+ return result;
+ }
+ }
+
+ public static class NestedStaticRecordWithMap {
+ private String id;
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel2RecordWithMap level2;
+ private Map level2Map;
+
+ public String getId() {
+ return id;
+ }
+
+ public NestedStaticRecordWithMap setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticRecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticRecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel2RecordWithMap getLevel2() {
+ return level2;
+ }
+
+ public NestedStaticRecordWithMap setLevel2(NestedStaticLevel2RecordWithMap level2) {
+ this.level2 = level2;
+ return this;
+ }
+
+ public Map getLevel2Map() {
+ return level2Map;
+ }
+
+ public NestedStaticRecordWithMap setLevel2Map(Map level2Map) {
+ this.level2Map = level2Map;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticRecordWithMap)) {
+ return false;
+ }
+
+ NestedStaticRecordWithMap that = (NestedStaticRecordWithMap) o;
+ return Objects.equals(id, that.id) && Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level2, that.level2) && Objects.equals(level2Map, that.level2Map);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(id);
+ result = 31 * result + Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level2);
+ result = 31 * result + Objects.hashCode(level2Map);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel2RecordWithMap {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel3RecordWithMap level3;
+ private Map level3Map;
+
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel2RecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel2RecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel3RecordWithMap getLevel3() {
+ return level3;
+ }
+
+ public NestedStaticLevel2RecordWithMap setLevel3(NestedStaticLevel3RecordWithMap level3) {
+ this.level3 = level3;
+ return this;
+ }
+
+ public Map getLevel3Map() {
+ return level3Map;
+ }
+
+ public NestedStaticLevel2RecordWithMap setLevel3Map(Map level3Map) {
+ this.level3Map = level3Map;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel2RecordWithMap)) {
+ return false;
+ }
+
+ NestedStaticLevel2RecordWithMap that = (NestedStaticLevel2RecordWithMap) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level3, that.level3) && Objects.equals(level3Map, that.level3Map);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level3);
+ result = 31 * result + Objects.hashCode(level3Map);
+ return result;
+ }
+ }
+
+ public static class NestedStaticLevel3RecordWithMap {
+ private String attr;
+ private Instant time;
+ private NestedStaticLevel4Record level4;
+ private Map level4Map;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel3RecordWithMap setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel3RecordWithMap setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ public NestedStaticLevel4Record getLevel4() {
+ return level4;
+ }
+
+ public NestedStaticLevel3RecordWithMap setLevel4(NestedStaticLevel4Record level4) {
+ this.level4 = level4;
+ return this;
+ }
+
+ public Map getLevel4Map() {
+ return level4Map;
+ }
+
+ public NestedStaticLevel3RecordWithMap setLevel4Map(Map level4Map) {
+ this.level4Map = level4Map;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel3RecordWithMap)) {
+ return false;
+ }
+
+ NestedStaticLevel3RecordWithMap that = (NestedStaticLevel3RecordWithMap) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time) && Objects.equals(level4, that.level4) && Objects.equals(level4Map, that.level4Map);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ result = 31 * result + Objects.hashCode(level4);
+ result = 31 * result + Objects.hashCode(level4Map);
+ return result;
+ }
+ }
+
+ // nested record used by list/set/map as the deepest nested level
+ public static class NestedStaticLevel4Record {
+ private String attr;
+ private Instant time;
+
+ public String getAttr() {
+ return attr;
+ }
+
+ public NestedStaticLevel4Record setAttr(String attr) {
+ this.attr = attr;
+ return this;
+ }
+
+ public Instant getTime() {
+ return time;
+ }
+
+ public NestedStaticLevel4Record setTime(Instant time) {
+ this.time = time;
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (!(o instanceof NestedStaticLevel4Record)) {
+ return false;
+ }
+
+ NestedStaticLevel4Record that = (NestedStaticLevel4Record) o;
+ return Objects.equals(attr, that.attr) && Objects.equals(time, that.time);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(attr);
+ result = 31 * result + Objects.hashCode(time);
+ return result;
+ }
+ }
+
+
+ // Bean Table Schemas for Simple Records
+ public static TableSchema buildBeanSchemaForSimpleRecordWithList() {
+ return BeanTableSchema.create(SimpleBeanRecordWithList.class);
+ }
+
+ public static TableSchema buildBeanSchemaForSimpleRecordWithSet() {
+ return BeanTableSchema.create(SimpleBeanRecordWithSet.class);
+ }
+
+ public static TableSchema buildBeanSchemaForSimpleRecordWithMap() {
+ return BeanTableSchema.create(SimpleBeanRecordWithMap.class);
+ }
+
+ // Bean Table Schemas for Nested Records
+ public static TableSchema buildBeanSchemaForNestedRecordWithList() {
+ return BeanTableSchema.create(NestedBeanRecordWithList.class);
+ }
+
+ public static TableSchema buildBeanSchemaForNestedRecordWithSet() {
+ return BeanTableSchema.create(NestedBeanRecordWithSet.class);
+ }
+
+ public static TableSchema buildBeanSchemaForNestedRecordWithMap() {
+ return BeanTableSchema.create(NestedBeanRecordWithMap.class);
+ }
+
+
+ // Immutable Table Schemas for Simple Records
+ public static TableSchema buildImmutableSchemaForSimpleRecordWithList() {
+ return ImmutableTableSchema.create(SimpleImmutableRecordWithList.class);
+ }
+
+ public static TableSchema buildImmutableSchemaForSimpleRecordWithSet() {
+ return ImmutableTableSchema.create(SimpleImmutableRecordWithSet.class);
+ }
+
+ public static TableSchema buildImmutableSchemaForSimpleRecordWithMap() {
+ return ImmutableTableSchema.create(SimpleImmutableRecordWithMap.class);
+ }
+
+
+ // Immutable Table Schemas for Nested Records
+ public static TableSchema buildImmutableSchemaForNestedRecordWithList() {
+ return ImmutableTableSchema.create(NestedImmutableRecordWithList.class);
+ }
+
+ public static TableSchema buildImmutableSchemaForNestedRecordWithSet() {
+ return ImmutableTableSchema.create(NestedImmutableRecordWithSet.class);
+ }
+
+ public static TableSchema buildImmutableSchemaForNestedRecordWithMap() {
+ return ImmutableTableSchema.create(NestedImmutableRecordWithMap.class);
+ }
+
+
+ // Static Table Schemas for Simple Records
+ public static TableSchema buildStaticSchemaForSimpleRecordWithList() {
+ return StaticTableSchema.builder(SimpleStaticRecordWithList.class)
+ .newItemSupplier(SimpleStaticRecordWithList::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleStaticRecordWithList::getId)
+ .setter(SimpleStaticRecordWithList::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleStaticRecordWithList::getAttr)
+ .setter(SimpleStaticRecordWithList::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleStaticRecordWithList::getTime)
+ .setter(SimpleStaticRecordWithList::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.listOf(EnhancedType.documentOf(
+ SimpleStaticChild.class,
+ buildStaticSchemaForSimpleChildRecord())),
+ a -> a.name("childList")
+ .getter(SimpleStaticRecordWithList::getChildList)
+ .setter(SimpleStaticRecordWithList::setChildList))
+ .build();
+ }
+
+ public static StaticTableSchema buildStaticSchemaForSimpleRecordWithSet() {
+ return StaticTableSchema.builder(SimpleStaticRecordWithSet.class)
+ .newItemSupplier(SimpleStaticRecordWithSet::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleStaticRecordWithSet::getId)
+ .setter(SimpleStaticRecordWithSet::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleStaticRecordWithSet::getAttr)
+ .setter(SimpleStaticRecordWithSet::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleStaticRecordWithSet::getTime)
+ .setter(SimpleStaticRecordWithSet::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.setOf(
+ EnhancedType.documentOf(
+ SimpleStaticChild.class,
+ buildStaticSchemaForSimpleChildRecord())),
+ a -> a.name("childSet")
+ .getter(SimpleStaticRecordWithSet::getChildSet)
+ .setter(SimpleStaticRecordWithSet::setChildSet))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForSimpleRecordWithMap() {
+ return StaticTableSchema.builder(SimpleStaticRecordWithMap.class)
+ .newItemSupplier(SimpleStaticRecordWithMap::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleStaticRecordWithMap::getId)
+ .setter(SimpleStaticRecordWithMap::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleStaticRecordWithMap::getAttr)
+ .setter(SimpleStaticRecordWithMap::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleStaticRecordWithMap::getTime)
+ .setter(SimpleStaticRecordWithMap::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, SimpleStaticChild.class),
+ a -> a.name("childMap")
+ .getter(SimpleStaticRecordWithMap::getChildMap)
+ .setter(SimpleStaticRecordWithMap::setChildMap))
+ .build();
+ }
+
+ // schema of the simple record used by list/set/map as the deepest simple level
+ public static TableSchema buildStaticSchemaForSimpleChildRecord() {
+ return StaticTableSchema.builder(SimpleStaticChild.class)
+ .newItemSupplier(SimpleStaticChild::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleStaticChild::getId)
+ .setter(SimpleStaticChild::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleStaticChild::getAttr)
+ .setter(SimpleStaticChild::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleStaticChild::getTime)
+ .setter(SimpleStaticChild::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .build();
+ }
+
+
+ // Static Table Schemas for Nested Records
+ public static TableSchema buildStaticSchemaForNestedRecordWithList() {
+ return StaticTableSchema.builder(NestedStaticRecordWithList.class)
+ .newItemSupplier(NestedStaticRecordWithList::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedStaticRecordWithList::getId)
+ .setter(NestedStaticRecordWithList::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticRecordWithList::getAttr)
+ .setter(NestedStaticRecordWithList::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticRecordWithList::getTime)
+ .setter(NestedStaticRecordWithList::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel2RecordWithList.class,
+ buildStaticSchemaForNestedLevel2RecordWithList()),
+ a -> a.name("level2")
+ .getter(NestedStaticRecordWithList::getLevel2)
+ .setter(NestedStaticRecordWithList::setLevel2))
+ .addAttribute(EnhancedType.listOf(EnhancedType.documentOf(
+ NestedStaticLevel2RecordWithList.class,
+ buildStaticSchemaForNestedLevel2RecordWithList())),
+ a -> a.name("level2List")
+ .getter(NestedStaticRecordWithList::getLevel2List)
+ .setter(NestedStaticRecordWithList::setLevel2List))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel2RecordWithList() {
+ return StaticTableSchema.builder(NestedStaticLevel2RecordWithList.class)
+ .newItemSupplier(NestedStaticLevel2RecordWithList::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel2RecordWithList::getAttr)
+ .setter(NestedStaticLevel2RecordWithList::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel2RecordWithList::getTime)
+ .setter(NestedStaticLevel2RecordWithList::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel3RecordWithList.class,
+ buildStaticSchemaForNestedLevel3RecordWithList()),
+ a -> a.name("level3")
+ .getter(NestedStaticLevel2RecordWithList::getLevel3)
+ .setter(NestedStaticLevel2RecordWithList::setLevel3))
+ .addAttribute(EnhancedType.listOf(EnhancedType.documentOf(
+ NestedStaticLevel3RecordWithList.class,
+ buildStaticSchemaForNestedLevel3RecordWithList())),
+ a -> a.name("level3List")
+ .getter(NestedStaticLevel2RecordWithList::getLevel3List)
+ .setter(NestedStaticLevel2RecordWithList::setLevel3List))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel3RecordWithList() {
+ return StaticTableSchema.builder(NestedStaticLevel3RecordWithList.class)
+ .newItemSupplier(NestedStaticLevel3RecordWithList::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel3RecordWithList::getAttr)
+ .setter(NestedStaticLevel3RecordWithList::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel3RecordWithList::getTime)
+ .setter(NestedStaticLevel3RecordWithList::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel4Record.class,
+ buildStaticSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedStaticLevel3RecordWithList::getLevel4)
+ .setter(NestedStaticLevel3RecordWithList::setLevel4))
+ .addAttribute(EnhancedType.listOf(
+ EnhancedType.documentOf(
+ NestedStaticLevel4Record.class,
+ buildStaticSchemaForNestedLevel4Record())),
+ a -> a.name("level4List")
+ .getter(NestedStaticLevel3RecordWithList::getLevel4List)
+ .setter(NestedStaticLevel3RecordWithList::setLevel4List))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedRecordWithSet() {
+ return StaticTableSchema.builder(NestedStaticRecordWithSet.class)
+ .newItemSupplier(NestedStaticRecordWithSet::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedStaticRecordWithSet::getId)
+ .setter(NestedStaticRecordWithSet::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticRecordWithSet::getAttr)
+ .setter(NestedStaticRecordWithSet::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticRecordWithSet::getTime)
+ .setter(NestedStaticRecordWithSet::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel2RecordWithSet.class,
+ buildStaticSchemaForNestedLevel2RecordWithSet()),
+ a -> a.name("level2")
+ .getter(NestedStaticRecordWithSet::getLevel2)
+ .setter(NestedStaticRecordWithSet::setLevel2))
+ .addAttribute(EnhancedType.setOf(EnhancedType.documentOf(
+ NestedStaticLevel2RecordWithSet.class,
+ buildStaticSchemaForNestedLevel2RecordWithSet())),
+ a -> a.name("level2Set")
+ .getter(NestedStaticRecordWithSet::getLevel2List)
+ .setter(NestedStaticRecordWithSet::setLevel2List))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel2RecordWithSet() {
+ return StaticTableSchema.builder(NestedStaticLevel2RecordWithSet.class)
+ .newItemSupplier(NestedStaticLevel2RecordWithSet::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel2RecordWithSet::getAttr)
+ .setter(NestedStaticLevel2RecordWithSet::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel2RecordWithSet::getTime)
+ .setter(NestedStaticLevel2RecordWithSet::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel3RecordWithSet.class,
+ buildStaticSchemaForNestedLevel3RecordWithSet()),
+ a -> a.name("level3")
+ .getter(NestedStaticLevel2RecordWithSet::getLevel3)
+ .setter(NestedStaticLevel2RecordWithSet::setLevel3))
+ .addAttribute(EnhancedType.setOf(EnhancedType.documentOf(
+ NestedStaticLevel3RecordWithSet.class,
+ buildStaticSchemaForNestedLevel3RecordWithSet())),
+ a -> a.name("level3Set")
+ .getter(NestedStaticLevel2RecordWithSet::getLevel3Set)
+ .setter(NestedStaticLevel2RecordWithSet::setLevel3Set))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel3RecordWithSet() {
+ return StaticTableSchema.builder(NestedStaticLevel3RecordWithSet.class)
+ .newItemSupplier(NestedStaticLevel3RecordWithSet::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel3RecordWithSet::getAttr)
+ .setter(NestedStaticLevel3RecordWithSet::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel3RecordWithSet::getTime)
+ .setter(NestedStaticLevel3RecordWithSet::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel4Record.class,
+ buildStaticSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedStaticLevel3RecordWithSet::getLevel4)
+ .setter(NestedStaticLevel3RecordWithSet::setLevel4))
+ .addAttribute(EnhancedType.setOf(
+ EnhancedType.documentOf(
+ NestedStaticLevel4Record.class,
+ buildStaticSchemaForNestedLevel4Record())),
+ a -> a.name("level4List")
+ .getter(NestedStaticLevel3RecordWithSet::getLevel4Set)
+ .setter(NestedStaticLevel3RecordWithSet::setLevel4Set))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedRecordWithMap() {
+ return StaticTableSchema.builder(NestedStaticRecordWithMap.class)
+ .newItemSupplier(NestedStaticRecordWithMap::new)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedStaticRecordWithMap::getId)
+ .setter(NestedStaticRecordWithMap::setId)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticRecordWithMap::getAttr)
+ .setter(NestedStaticRecordWithMap::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticRecordWithMap::getTime)
+ .setter(NestedStaticRecordWithMap::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel2RecordWithMap.class,
+ buildStaticSchemaForNestedLevel2RecordWithMap()),
+ a -> a.name("level2")
+ .getter(NestedStaticRecordWithMap::getLevel2)
+ .setter(NestedStaticRecordWithMap::setLevel2))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedStaticLevel2RecordWithMap.class),
+ a -> a.name("level2Map")
+ .getter(NestedStaticRecordWithMap::getLevel2Map)
+ .setter(NestedStaticRecordWithMap::setLevel2Map))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel2RecordWithMap() {
+ return StaticTableSchema.builder(NestedStaticLevel2RecordWithMap.class)
+ .newItemSupplier(NestedStaticLevel2RecordWithMap::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel2RecordWithMap::getAttr)
+ .setter(NestedStaticLevel2RecordWithMap::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel2RecordWithMap::getTime)
+ .setter(NestedStaticLevel2RecordWithMap::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel3RecordWithMap.class,
+ buildStaticSchemaForNestedLevel3RecordWithMap()),
+ a -> a.name("level3")
+ .getter(NestedStaticLevel2RecordWithMap::getLevel3)
+ .setter(NestedStaticLevel2RecordWithMap::setLevel3))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedStaticLevel3RecordWithMap.class),
+ a -> a.name("level3Map")
+ .getter(NestedStaticLevel2RecordWithMap::getLevel3Map)
+ .setter(NestedStaticLevel2RecordWithMap::setLevel3Map))
+ .build();
+ }
+
+ public static TableSchema buildStaticSchemaForNestedLevel3RecordWithMap() {
+ return StaticTableSchema.builder(NestedStaticLevel3RecordWithMap.class)
+ .newItemSupplier(NestedStaticLevel3RecordWithMap::new)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedStaticLevel3RecordWithMap::getAttr)
+ .setter(NestedStaticLevel3RecordWithMap::setAttr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedStaticLevel3RecordWithMap::getTime)
+ .setter(NestedStaticLevel3RecordWithMap::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedStaticLevel4Record.class,
+ buildStaticSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedStaticLevel3RecordWithMap::getLevel4)
+ .setter(NestedStaticLevel3RecordWithMap::setLevel4))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedStaticLevel4Record.class),
+ a -> a.name("level4Map")
+ .getter(NestedStaticLevel3RecordWithMap::getLevel4Map)
+ .setter(NestedStaticLevel3RecordWithMap::setLevel4Map))
+ .build();
+ }
+
+ // schema of the nested record used by list/set/map as the deepest nested level
+ public static TableSchema buildStaticSchemaForNestedLevel4Record() {
+ return StaticTableSchema.builder(NestedStaticLevel4Record.class)
+ .newItemSupplier(NestedStaticLevel4Record::new)
+ .addAttribute(String.class,
+ x -> x.name("attr")
+ .getter(NestedStaticLevel4Record::getAttr)
+ .setter(NestedStaticLevel4Record::setAttr))
+ .addAttribute(Instant.class,
+ x -> x.name("time")
+ .getter(NestedStaticLevel4Record::getTime)
+ .setter(NestedStaticLevel4Record::setTime)
+ .tags(autoGeneratedTimestampAttribute()))
+ .build();
+ }
+
+
+ // Static Immutable Table Schemas for Simple Records
+ public static TableSchema buildStaticImmutableSchemaForSimpleChildRecord() {
+ return StaticImmutableTableSchema.builder(SimpleImmutableChild.class, SimpleImmutableChild.Builder.class)
+ .newItemBuilder(SimpleImmutableChild::builder, SimpleImmutableChild.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleImmutableChild::getId)
+ .setter(SimpleImmutableChild.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleImmutableChild::getAttr)
+ .setter(SimpleImmutableChild.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleImmutableChild::getTime)
+ .setter(SimpleImmutableChild.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForSimpleRecordWithList() {
+ return StaticImmutableTableSchema.builder(SimpleImmutableRecordWithList.class,
+ SimpleImmutableRecordWithList.Builder.class)
+ .newItemBuilder(SimpleImmutableRecordWithList::builder,
+ SimpleImmutableRecordWithList.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleImmutableRecordWithList::getId)
+ .setter(SimpleImmutableRecordWithList.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleImmutableRecordWithList::getAttr)
+ .setter(SimpleImmutableRecordWithList.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleImmutableRecordWithList::getTime)
+ .setter(SimpleImmutableRecordWithList.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.listOf(
+ EnhancedType.documentOf(
+ SimpleImmutableChild.class,
+ buildStaticImmutableSchemaForSimpleChildRecord())),
+ a -> a.name("childList")
+ .getter(SimpleImmutableRecordWithList::getChildList)
+ .setter(SimpleImmutableRecordWithList.Builder::childList))
+ .build();
+ }
+
+ public static StaticImmutableTableSchema buildStaticImmutableSchemaForSimpleRecordWithSet() {
+ return StaticImmutableTableSchema.builder(SimpleImmutableRecordWithSet.class, SimpleImmutableRecordWithSet.Builder.class)
+ .newItemBuilder(SimpleImmutableRecordWithSet::builder,
+ SimpleImmutableRecordWithSet.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleImmutableRecordWithSet::getId)
+ .setter(SimpleImmutableRecordWithSet.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleImmutableRecordWithSet::getAttr)
+ .setter(SimpleImmutableRecordWithSet.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleImmutableRecordWithSet::getTime)
+ .setter(SimpleImmutableRecordWithSet.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.setOf(
+ EnhancedType.documentOf(
+ SimpleImmutableChild.class,
+ buildStaticImmutableSchemaForSimpleChildRecord())),
+ a -> a.name("childSet")
+ .getter(SimpleImmutableRecordWithSet::getChildSet)
+ .setter(SimpleImmutableRecordWithSet.Builder::childSet))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForSimpleRecordWithMap() {
+ return StaticImmutableTableSchema.builder(SimpleImmutableRecordWithMap.class, SimpleImmutableRecordWithMap.Builder.class)
+ .newItemBuilder(SimpleImmutableRecordWithMap::builder,
+ SimpleImmutableRecordWithMap.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(SimpleImmutableRecordWithMap::getId)
+ .setter(SimpleImmutableRecordWithMap.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(SimpleImmutableRecordWithMap::getAttr)
+ .setter(SimpleImmutableRecordWithMap.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(SimpleImmutableRecordWithMap::getTime)
+ .setter(SimpleImmutableRecordWithMap.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, SimpleImmutableChild.class),
+ a -> a.name("childMap")
+ .getter(SimpleImmutableRecordWithMap::getChildMap)
+ .setter(SimpleImmutableRecordWithMap.Builder::childMap))
+ .build();
+ }
+
+
+ // Static Immutable Table Schemas for Nested Records
+ public static TableSchema buildStaticImmutableSchemaForNestedRecordWithList() {
+ return StaticImmutableTableSchema.builder(NestedImmutableRecordWithList.class,
+ NestedImmutableRecordWithList.Builder.class)
+ .newItemBuilder(NestedImmutableRecordWithList::builder,
+ NestedImmutableRecordWithList.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedImmutableRecordWithList::getId)
+ .setter(NestedImmutableRecordWithList.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableRecordWithList::getAttr)
+ .setter(NestedImmutableRecordWithList.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableRecordWithList::getTime)
+ .setter(NestedImmutableRecordWithList.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel2RecordWithList.class,
+ buildStaticImmutableSchemaForNestedLevel2RecordWithList()),
+ a -> a.name("level2")
+ .getter(NestedImmutableRecordWithList::getLevel2)
+ .setter(NestedImmutableRecordWithList.Builder::level2))
+ .addAttribute(EnhancedType.listOf(
+ EnhancedType.documentOf(
+ NestedImmutableLevel2RecordWithList.class,
+ buildStaticImmutableSchemaForNestedLevel2RecordWithList())),
+ a -> a.name("level2List")
+ .getter(NestedImmutableRecordWithList::getLevel2List)
+ .setter(NestedImmutableRecordWithList.Builder::level2List))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel2RecordWithList() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel2RecordWithList.class,
+ NestedImmutableLevel2RecordWithList.Builder.class)
+ .newItemBuilder(NestedImmutableLevel2RecordWithList::builder,
+ NestedImmutableLevel2RecordWithList.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel2RecordWithList::getAttr)
+ .setter(NestedImmutableLevel2RecordWithList.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel2RecordWithList::getTime)
+ .setter(NestedImmutableLevel2RecordWithList.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel3RecordWithList.class,
+ buildStaticImmutableSchemaForNestedLevel3RecordWithList()),
+ a -> a.name("level3")
+ .getter(NestedImmutableLevel2RecordWithList::getLevel3)
+ .setter(NestedImmutableLevel2RecordWithList.Builder::level3))
+ .addAttribute(EnhancedType.listOf(
+ EnhancedType.documentOf(
+ NestedImmutableLevel3RecordWithList.class,
+ buildStaticImmutableSchemaForNestedLevel3RecordWithList())),
+ a -> a.name("level3List")
+ .getter(NestedImmutableLevel2RecordWithList::getLevel3List)
+ .setter(NestedImmutableLevel2RecordWithList.Builder::level3List))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel3RecordWithList() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel3RecordWithList.class,
+ NestedImmutableLevel3RecordWithList.Builder.class)
+ .newItemBuilder(NestedImmutableLevel3RecordWithList::builder,
+ NestedImmutableLevel3RecordWithList.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel3RecordWithList::getAttr)
+ .setter(NestedImmutableLevel3RecordWithList.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel3RecordWithList::getTime)
+ .setter(NestedImmutableLevel3RecordWithList.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel4Record.class,
+ buildStaticImmutableSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedImmutableLevel3RecordWithList::getLevel4)
+ .setter(NestedImmutableLevel3RecordWithList.Builder::level4))
+ .addAttribute(EnhancedType.listOf(
+ EnhancedType.documentOf(
+ NestedImmutableLevel4Record.class,
+ buildStaticImmutableSchemaForNestedLevel4Record())),
+ a -> a.name("level4List")
+ .getter(NestedImmutableLevel3RecordWithList::getLevel4List)
+ .setter(NestedImmutableLevel3RecordWithList.Builder::level4List))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedRecordWithSet() {
+ return StaticImmutableTableSchema.builder(NestedImmutableRecordWithSet.class,
+ NestedImmutableRecordWithSet.Builder.class)
+ .newItemBuilder(NestedImmutableRecordWithSet::builder,
+ NestedImmutableRecordWithSet.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedImmutableRecordWithSet::getId)
+ .setter(NestedImmutableRecordWithSet.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableRecordWithSet::getAttr)
+ .setter(NestedImmutableRecordWithSet.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableRecordWithSet::getTime)
+ .setter(NestedImmutableRecordWithSet.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel2RecordWithSet.class,
+ buildStaticImmutableSchemaForNestedLevel2RecordWithSet()),
+ a -> a.name("level2")
+ .getter(NestedImmutableRecordWithSet::getLevel2)
+ .setter(NestedImmutableRecordWithSet.Builder::level2))
+ .addAttribute(EnhancedType.setOf(
+ EnhancedType.documentOf(
+ NestedImmutableLevel2RecordWithSet.class,
+ buildStaticImmutableSchemaForNestedLevel2RecordWithSet())),
+ a -> a.name("level2Set")
+ .getter(NestedImmutableRecordWithSet::getLevel2Set)
+ .setter(NestedImmutableRecordWithSet.Builder::level2Set))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel2RecordWithSet() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel2RecordWithSet.class,
+ NestedImmutableLevel2RecordWithSet.Builder.class)
+ .newItemBuilder(NestedImmutableLevel2RecordWithSet::builder,
+ NestedImmutableLevel2RecordWithSet.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel2RecordWithSet::getAttr)
+ .setter(NestedImmutableLevel2RecordWithSet.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel2RecordWithSet::getTime)
+ .setter(NestedImmutableLevel2RecordWithSet.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel3RecordWithSet.class,
+ buildStaticImmutableSchemaForNestedLevel3RecordWithSet()),
+ a -> a.name("level3")
+ .getter(NestedImmutableLevel2RecordWithSet::getLevel3)
+ .setter(NestedImmutableLevel2RecordWithSet.Builder::level3))
+ .addAttribute(EnhancedType.setOf(
+ EnhancedType.documentOf(
+ NestedImmutableLevel3RecordWithSet.class,
+ buildStaticImmutableSchemaForNestedLevel3RecordWithSet())),
+ a -> a.name("level3Set")
+ .getter(NestedImmutableLevel2RecordWithSet::getLevel3Set)
+ .setter(NestedImmutableLevel2RecordWithSet.Builder::level3Set))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel3RecordWithSet() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel3RecordWithSet.class,
+ NestedImmutableLevel3RecordWithSet.Builder.class)
+ .newItemBuilder(NestedImmutableLevel3RecordWithSet::builder,
+ NestedImmutableLevel3RecordWithSet.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel3RecordWithSet::getAttr)
+ .setter(NestedImmutableLevel3RecordWithSet.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel3RecordWithSet::getTime)
+ .setter(NestedImmutableLevel3RecordWithSet.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel4Record.class,
+ buildStaticImmutableSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedImmutableLevel3RecordWithSet::getLevel4)
+ .setter(NestedImmutableLevel3RecordWithSet.Builder::level4))
+ .addAttribute(EnhancedType.setOf(EnhancedType.documentOf(NestedImmutableLevel4Record.class,
+ buildStaticImmutableSchemaForNestedLevel4Record())),
+ a -> a.name("level4Set")
+ .getter(NestedImmutableLevel3RecordWithSet::getLevel4Set)
+ .setter(NestedImmutableLevel3RecordWithSet.Builder::level4Set))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedRecordWithMap() {
+ return StaticImmutableTableSchema.builder(NestedImmutableRecordWithMap.class,
+ NestedImmutableRecordWithMap.Builder.class)
+ .newItemBuilder(NestedImmutableRecordWithMap::builder,
+ NestedImmutableRecordWithMap.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("id")
+ .getter(NestedImmutableRecordWithMap::getId)
+ .setter(NestedImmutableRecordWithMap.Builder::id)
+ .tags(primaryPartitionKey()))
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableRecordWithMap::getAttr)
+ .setter(NestedImmutableRecordWithMap.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableRecordWithMap::getTime)
+ .setter(NestedImmutableRecordWithMap.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel2RecordWithMap.class,
+ buildStaticImmutableSchemaForNestedLevel2RecordWithMap()),
+ a -> a.name("level2")
+ .getter(NestedImmutableRecordWithMap::getLevel2)
+ .setter(NestedImmutableRecordWithMap.Builder::level2))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedImmutableLevel2RecordWithMap.class),
+ a -> a.name("childMap")
+ .getter(NestedImmutableRecordWithMap::getLevel2Map)
+ .setter(NestedImmutableRecordWithMap.Builder::level2Map))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel2RecordWithMap() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel2RecordWithMap.class,
+ NestedImmutableLevel2RecordWithMap.Builder.class)
+ .newItemBuilder(NestedImmutableLevel2RecordWithMap::builder,
+ NestedImmutableLevel2RecordWithMap.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel2RecordWithMap::getAttr)
+ .setter(NestedImmutableLevel2RecordWithMap.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel2RecordWithMap::getTime)
+ .setter(NestedImmutableLevel2RecordWithMap.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel3RecordWithMap.class,
+ buildStaticImmutableSchemaForNestedLevel3RecordWithMap()),
+ a -> a.name("level3")
+ .getter(NestedImmutableLevel2RecordWithMap::getLevel3)
+ .setter(NestedImmutableLevel2RecordWithMap.Builder::level3))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedImmutableLevel3RecordWithMap.class),
+ a -> a.name("childMap")
+ .getter(NestedImmutableLevel2RecordWithMap::getLevel3Map)
+ .setter(NestedImmutableLevel2RecordWithMap.Builder::level3Map))
+ .build();
+ }
+
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel3RecordWithMap() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel3RecordWithMap.class,
+ NestedImmutableLevel3RecordWithMap.Builder.class)
+ .newItemBuilder(NestedImmutableLevel3RecordWithMap::builder,
+ NestedImmutableLevel3RecordWithMap.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel3RecordWithMap::getAttr)
+ .setter(NestedImmutableLevel3RecordWithMap.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel3RecordWithMap::getTime)
+ .setter(NestedImmutableLevel3RecordWithMap.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .addAttribute(EnhancedType.documentOf(
+ NestedImmutableLevel4Record.class,
+ buildStaticImmutableSchemaForNestedLevel4Record()),
+ a -> a.name("level4")
+ .getter(NestedImmutableLevel3RecordWithMap::getLevel4)
+ .setter(NestedImmutableLevel3RecordWithMap.Builder::level4))
+ .addAttribute(EnhancedType.mapOf(
+ String.class, NestedImmutableLevel4Record.class),
+ a -> a.name("level4Map")
+ .getter(NestedImmutableLevel3RecordWithMap::getLevel4Map)
+ .setter(NestedImmutableLevel3RecordWithMap.Builder::level4Map))
+ .build();
+ }
+
+ // schema of the record used by list/set/map as the deepest nested level
+ public static TableSchema buildStaticImmutableSchemaForNestedLevel4Record() {
+ return StaticImmutableTableSchema.builder(NestedImmutableLevel4Record.class, NestedImmutableLevel4Record.Builder.class)
+ .newItemBuilder(NestedImmutableLevel4Record::builder,
+ NestedImmutableLevel4Record.Builder::build)
+ .addAttribute(String.class,
+ a -> a.name("attr")
+ .getter(NestedImmutableLevel4Record::getAttr)
+ .setter(NestedImmutableLevel4Record.Builder::attr))
+ .addAttribute(Instant.class,
+ a -> a.name("time")
+ .getter(NestedImmutableLevel4Record::getTime)
+ .setter(NestedImmutableLevel4Record.Builder::time)
+ .tags(autoGeneratedTimestampAttribute()))
+ .build();
+ }
+
+
+ // Object builder methods
+ public static SimpleBeanRecordWithList buildSimpleBeanRecordWithList() {
+ return new SimpleBeanRecordWithList()
+ .setId(ID_1).setAttr(ATTR_LEVEL1)
+ .setChildList(new ArrayList<>(Arrays.asList(
+ new SimpleBeanChild().setId(ID_1).setAttr(ATTR_CHILD1),
+ new SimpleBeanChild().setId(ID_2).setAttr(ATTR_CHILD2))));
+ }
+
+ public static SimpleStaticRecordWithList buildSimpleStaticRecordWithList() {
+ return new SimpleStaticRecordWithList()
+ .setId(ID_1).setAttr(ATTR_LEVEL1)
+ .setChildList(new ArrayList<>(Arrays.asList(
+ new SimpleStaticChild().setId(ID_1).setAttr(ATTR_CHILD1),
+ new SimpleStaticChild().setId(ID_2).setAttr(ATTR_CHILD2))));
+ }
+
+ public static SimpleImmutableRecordWithList buildSimpleImmutableRecordWithList() {
+ return SimpleImmutableRecordWithList.builder()
+ .id(ID_1).attr(ATTR_LEVEL1)
+ .childList(new ArrayList<>(Arrays.asList(
+ SimpleImmutableChild.builder().id(ID_1).attr(ATTR_CHILD1).build(),
+ SimpleImmutableChild.builder().id(ID_2).attr(ATTR_CHILD2).build())))
+ .build();
+ }
+
+ public static NestedBeanRecordWithList buildNestedBeanRecordWithList() {
+ NestedBeanLevel4Record level4 =
+ new NestedBeanLevel4Record()
+ .setId(ID_1)
+ .setAttr(ATTR_LEVEL4);
+
+ NestedBeanLevel3RecordWithList level3 =
+ new NestedBeanLevel3RecordWithList()
+ .setAttr(ATTR_LEVEL3)
+ .setLevel4(level4)
+ .setLevel4List(new ArrayList<>(singletonList(level4)));
+
+ NestedBeanLevel2RecordWithList level2 =
+ new NestedBeanLevel2RecordWithList()
+ .setAttr(ATTR_LEVEL2).setLevel3(level3)
+ .setLevel3List(new ArrayList<>(singletonList(level3)));
+
+ return new NestedBeanRecordWithList()
+ .setId(ID_1).setAttr(ATTR_LEVEL1)
+ .setLevel2(level2)
+ .setLevel2List(new ArrayList<>(singletonList(level2)));
+ }
+
+ public static NestedStaticRecordWithList buildNestedStaticRecordWithList() {
+ NestedStaticLevel4Record level4 =
+ new NestedStaticLevel4Record()
+ .setAttr(ATTR_LEVEL4);
+
+ NestedStaticLevel3RecordWithList level3 =
+ new NestedStaticLevel3RecordWithList()
+ .setAttr(ATTR_LEVEL3)
+ .setLevel4(level4)
+ .setLevel4List(new ArrayList<>(singletonList(level4)));
+
+ NestedStaticLevel2RecordWithList level2 =
+ new NestedStaticLevel2RecordWithList()
+ .setAttr(ATTR_LEVEL2)
+ .setLevel3(level3)
+ .setLevel3List(new ArrayList<>(singletonList(level3)));
+
+ return new NestedStaticRecordWithList()
+ .setId(ID_1)
+ .setAttr(ATTR_LEVEL1)
+ .setLevel2(level2)
+ .setLevel2List(new ArrayList<>(singletonList(level2)));
+ }
+
+ public static NestedImmutableRecordWithList buildNestedImmutableRecordWithList() {
+ NestedImmutableLevel4Record level4 =
+ NestedImmutableLevel4Record.builder()
+ .id(ID_ATTR)
+ .attr(ATTR_LEVEL4)
+ .build();
+
+ NestedImmutableLevel3RecordWithList level3 =
+ NestedImmutableLevel3RecordWithList.builder()
+ .attr(ATTR_LEVEL3)
+ .level4(level4)
+ .level4List(new ArrayList<>(singletonList(level4)))
+ .build();
+
+ NestedImmutableLevel2RecordWithList level2 =
+ NestedImmutableLevel2RecordWithList.builder()
+ .attr(ATTR_LEVEL2)
+ .level3(level3)
+ .level3List(new ArrayList<>(singletonList(level3)))
+ .build();
+
+ return NestedImmutableRecordWithList.builder()
+ .id(ID_1)
+ .attr(ATTR_LEVEL1).level2(level2)
+ .level2List(new ArrayList<>(singletonList(level2)))
+ .build();
+ }
+
+ public static SimpleBeanRecordWithMap buildSimpleBeanRecordWithMap() {
+ return new SimpleBeanRecordWithMap()
+ .setId(ID_1)
+ .setAttr(ATTR_LEVEL1)
+ .setChildMap(ImmutableMap.of(
+ CHILD1_KEY, new SimpleBeanChild().setId(ID_1).setAttr(ATTR_CHILD1),
+ CHILD2_KEY, new SimpleBeanChild().setId(ID_2).setAttr(ATTR_CHILD2)
+ ));
+ }
+
+ public static NestedBeanRecordWithMap buildNestedBeanRecordWithMap() {
+ NestedBeanLevel4Record level4 =
+ new NestedBeanLevel4Record()
+ .setId(ID_1)
+ .setAttr(ATTR_LEVEL4);
+
+ NestedBeanLevel3RecordWithMap level3 =
+ new NestedBeanLevel3RecordWithMap()
+ .setAttr(ATTR_LEVEL3)
+ .setLevel4(level4)
+ .setLevel4Map(ImmutableMap.of(LEVEL4_KEY, level4));
+
+ NestedBeanLevel2RecordWithMap level2 =
+ new NestedBeanLevel2RecordWithMap()
+ .setAttr(ATTR_LEVEL2)
+ .setLevel3(level3)
+ .setLevel3Map(ImmutableMap.of(LEVEL3_KEY, level3));
+
+ return new NestedBeanRecordWithMap()
+ .setId(ID_1)
+ .setAttr(ATTR_LEVEL1)
+ .setLevel2(level2)
+ .setLevel2Map(ImmutableMap.of(LEVEL2_KEY, level2));
+ }
+
+ public static SimpleImmutableRecordWithMap buildSimpleImmutableRecordWithMap() {
+ return SimpleImmutableRecordWithMap.builder()
+ .id(ID_1).attr(ATTR_LEVEL1)
+ .childMap(ImmutableMap.of(
+ CHILD1_KEY, SimpleImmutableChild.builder().id(ID_1).attr(ATTR_CHILD1).build(),
+ CHILD2_KEY, SimpleImmutableChild.builder().id(ID_2).attr(ATTR_CHILD2).build()))
+ .build();
+ }
+
+ public static NestedImmutableRecordWithMap buildNestedImmutableRecordWithMap() {
+ NestedImmutableLevel4Record level4 =
+ NestedImmutableLevel4Record.builder()
+ .id(ID_1)
+ .attr(ATTR_LEVEL4)
+ .build();
+
+ NestedImmutableLevel3RecordWithMap level3 =
+ NestedImmutableLevel3RecordWithMap.builder()
+ .attr(ATTR_LEVEL3)
+ .level4(level4)
+ .level4Map(new HashMap<>(singletonMap(LEVEL4_KEY, level4)))
+ .build();
+
+ NestedImmutableLevel2RecordWithMap level2 =
+ NestedImmutableLevel2RecordWithMap.builder()
+ .attr(ATTR_LEVEL2)
+ .level3(level3)
+ .level3Map(new HashMap<>(singletonMap(LEVEL3_KEY, level3)))
+ .build();
+
+ return NestedImmutableRecordWithMap.builder()
+ .id(ID_1)
+ .attr(ATTR_LEVEL1)
+ .level2(level2)
+ .level2Map(new HashMap<>(singletonMap(LEVEL2_KEY, level2)))
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordListElement.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordListElement.java
new file mode 100644
index 000000000000..6cf9450f349c
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordListElement.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package software.amazon.awssdk.enhanced.dynamodb.functionaltests.models;
+
+import java.time.Instant;
+import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedTimestampAttribute;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
+import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
+
+@DynamoDbBean
+public class NestedRecordListElement {
+ private String id;
+ private String attribute;
+ private Instant timeAttributeElement;
+
+ @DynamoDbPartitionKey
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(String attribute) {
+ this.attribute = attribute;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getTimeAttributeElement() {
+ return timeAttributeElement;
+ }
+
+ public void setTimeAttributeElement(Instant timeAttributeElement) {
+ this.timeAttributeElement = timeAttributeElement;
+ }
+}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordWithUpdateBehavior.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordWithUpdateBehavior.java
index 883a89813c1a..df2e92c57392 100644
--- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordWithUpdateBehavior.java
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/NestedRecordWithUpdateBehavior.java
@@ -18,6 +18,7 @@
import static software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior.WRITE_IF_NOT_EXISTS;
import java.time.Instant;
+import java.util.List;
import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAtomicCounter;
import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedTimestampAttribute;
import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbVersionAttribute;
@@ -30,10 +31,12 @@ public class NestedRecordWithUpdateBehavior {
private String id;
private String nestedUpdateBehaviorAttribute;
private Long nestedVersionedAttribute;
- private Instant nestedTimeAttribute;
+ private Instant nestedCreatedTimeAttribute;
+ private Instant nestedUpdatedTimeAttribute;
private Long nestedCounter;
private NestedRecordWithUpdateBehavior nestedRecord;
private String attribute;
+ private List nestedRecordList;
@DynamoDbPartitionKey
public String getId() {
@@ -63,12 +66,22 @@ public void setNestedVersionedAttribute(Long nestedVersionedAttribute) {
}
@DynamoDbAutoGeneratedTimestampAttribute
- public Instant getNestedTimeAttribute() {
- return nestedTimeAttribute;
+ @DynamoDbUpdateBehavior(WRITE_IF_NOT_EXISTS)
+ public Instant getNestedCreatedTimeAttribute() {
+ return nestedCreatedTimeAttribute;
}
- public void setNestedTimeAttribute(Instant nestedTimeAttribute) {
- this.nestedTimeAttribute = nestedTimeAttribute;
+ public void setNestedCreatedTimeAttribute(Instant nestedCreatedTimeAttribute) {
+ this.nestedCreatedTimeAttribute = nestedCreatedTimeAttribute;
+ }
+
+ @DynamoDbAutoGeneratedTimestampAttribute
+ public Instant getNestedUpdatedTimeAttribute() {
+ return nestedUpdatedTimeAttribute;
+ }
+
+ public void setNestedUpdatedTimeAttribute(Instant nestedUpdatedTimeAttribute) {
+ this.nestedUpdatedTimeAttribute = nestedUpdatedTimeAttribute;
}
@DynamoDbAtomicCounter
@@ -95,4 +108,10 @@ public String getAttribute() {
public void setAttribute(String attribute) {
this.attribute = attribute;
}
+
+ public List getNestedRecordList() { return nestedRecordList;}
+
+ public void setNestedRecordList(List nestedRecordList) {
+ this.nestedRecordList = nestedRecordList;
+ }
}
diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/RecordWithUpdateBehaviors.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/RecordWithUpdateBehaviors.java
index 8bd874fee002..ad396ed28d00 100644
--- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/RecordWithUpdateBehaviors.java
+++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/models/RecordWithUpdateBehaviors.java
@@ -15,7 +15,10 @@
package software.amazon.awssdk.enhanced.dynamodb.functionaltests.models;
+import static software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior.WRITE_IF_NOT_EXISTS;
+
import java.time.Instant;
+import java.util.List;
import software.amazon.awssdk.enhanced.dynamodb.converters.EpochMillisFormatTestConverter;
import software.amazon.awssdk.enhanced.dynamodb.converters.TimeFormatUpdateTestConverter;
import software.amazon.awssdk.enhanced.dynamodb.extensions.annotations.DynamoDbAutoGeneratedTimestampAttribute;
@@ -26,8 +29,6 @@
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbUpdateBehavior;
-import static software.amazon.awssdk.enhanced.dynamodb.mapper.UpdateBehavior.WRITE_IF_NOT_EXISTS;
-
@DynamoDbBean
public class RecordWithUpdateBehaviors {
private String id;
@@ -40,6 +41,7 @@ public class RecordWithUpdateBehaviors {
private Instant formattedLastAutoUpdatedOn;
private NestedRecordWithUpdateBehavior nestedRecord;
private String key;
+ private List nestedRecordList;
@DynamoDbPartitionKey
public String getId() {
@@ -133,4 +135,10 @@ public NestedRecordWithUpdateBehavior getNestedRecord() {
public void setNestedRecord(NestedRecordWithUpdateBehavior nestedRecord) {
this.nestedRecord = nestedRecord;
}
+
+ public List getNestedRecordList() { return nestedRecordList;}
+
+ public void setNestedRecordList(List nestedRecordList) {
+ this.nestedRecordList = nestedRecordList;
+ }
}