Skip to content

Add support for strategy-based UUID auto-generation (@DynamoDbAutoGeneratedUuid) - #7377

Closed
RanVaknin wants to merge 37 commits into
masterfrom
pr-6373-ci
Closed

RanVaknin wants to merge 37 commits into
masterfrom
pr-6373-ci

Conversation

@RanVaknin

Copy link
Copy Markdown
Contributor

CI mirror of #6373 (by @anasatirbasa). Opened from an in-repo branch so GitHub Actions CI runs. Not for merge — review/merge happens on #6373.


Description

This PR adds strategy-based UUID generation to @DynamoDbAutoGeneratedUuid, following the V1 strategy model from @DynamoDBGeneratedUuid(DynamoDBAutoGenerateStrategy):

  • strategy = ALWAYS (default): generate a new UUID on every write
  • strategy = CREATE: generate a UUID only when the attribute is missing

For a non-key CREATE attribute omitted from an updateItem, the extension lets DynamoDB check the stored item:

SET attribute = if_not_exists(attribute, generatedUuid)

This prevents a partial update from overwriting an existing value just because the caller omitted the field.

Important Rules

  • @DynamoDbAutoGeneratedUuid is valid only for String attributes.
  • CREATE works for key and non-key attributes. A missing primary key is generated in the item map because updateItem requires it.
  • ALWAYS remains the default, preserving existing behavior.
  • In the write item map, DynamoDB NULL is missing and an empty string is present.
  • putItem is a full replacement. Repeating a put with the same key and no CREATE value generates a new UUID.
  • Nested UUID fields and nested @DynamoDbIgnoreNulls behavior are out of scope. Flattened UUID attributes are supported.

Ignore-Null Behavior for a Top-Level, Non-Key CREATE UUID

Request mapping Write item representation Stored attribute Result
ignoreNulls(true) UUID attribute omitted Existing UUID or other value Stored value is preserved
ignoreNulls(true) UUID attribute omitted Absent A UUID is generated by if_not_exists
ignoreNulls(true) UUID attribute omitted DynamoDB NULL Stored NULL is preserved because the attribute exists
ignoreNulls(true) UUID attribute omitted Empty string Empty string is preserved
IgnoreNullsMode.MAPS_ONLY Top-level null UUID omitted Existing / absent / NULL / empty Existing values, stored NULL, and empty strings are preserved; a UUID is generated only when the stored attribute is absent
IgnoreNullsMode.SCALAR_ONLY Top-level null UUID omitted Existing / absent / NULL / empty Existing values, stored NULL, and empty strings are preserved; a UUID is generated only when the stored attribute is absent
ignoreNulls(false) Explicit DynamoDB NULL Any stored state A UUID is generated before the request and written normally
IgnoreNullsMode.DEFAULT Explicit DynamoDB NULL Any stored state A UUID is generated before the request and written normally

UpdateBehavior Notes

@DynamoDbUpdateBehavior affects UpdateItem expression construction, not UUID strategy selection or PutItem. Omitted non-key CREATE values use the extension's if_not_exists action; caller-provided values use the normal update expression.


Motivation and Context

This PR addresses #5497. Instead of adding a separate @DynamoDbAutoGeneratedKey API, it adds ALWAYS and CREATE strategies to the existing UUID annotation. ALWAYS stays the default for backward compatibility, while CREATE provides generate-only-when-missing behavior.


Modifications

  • Added DynamoDbAutoGenerateStrategy with ALWAYS and CREATE.
  • Added strategy to @DynamoDbAutoGeneratedUuid, defaulting to ALWAYS.
  • Updated AutoGeneratedUuidTag and AutoGeneratedUuidExtension to carry and apply the strategy per attribute.
  • Added a server-side if_not_exists SET action for omitted, non-key CREATE fields during updateItem. Multiple omitted fields are supported.
  • Kept missing primary-key generation in the transformed item.
  • Removed the separate AutoGeneratedKey implementation path.
  • Updated Javadocs and examples, and added the required japicmp exclusion for the new annotation method.

Testing

Tests cover:

  • ALWAYS and CREATE for key and non-key attributes.
  • Present, absent, DynamoDB NULL, and empty-string values.
  • ignoreNulls(true/false) and all IgnoreNullsMode values.
  • Stored-value preservation and generation through sync, async, and transaction updates.
  • Repeated putItem, multiple omitted CREATE attributes, update behavior, extension ordering, shared clients, flattened schemas, and mixed-schema batch/transaction operations.

Local results:

  • UUID-focused suite: 114 tests, 0 failures/errors
  • Full dynamodb-enhanced module: 3,637 tests, 0 failures/errors
  • Checkstyle: 0 violations

Test Coverage on modified classes:

image

Test Coverage Checklist

Scenario Done Comments if Not Done
1. Different TableSchema Creation Methods
a. TableSchema.fromBean(Customer.class) [x]
b. TableSchema.fromImmutableClass(Customer.class) for immutable classes [x]
c. TableSchema.documentSchemaBuilder().build() [x] No UUID tags; extension no-ops
d. StaticTableSchema.builder(Customer.class) [x]
2. Nesting of Different TableSchema Types
a. @DynamoDbBean with nested @DynamoDbBean as NonNull [ ] UUID generation does not recurse into nested documents; flattened UUID coverage is in 10a/10d
b. @DynamoDbBean with nested @DynamoDbImmutable as NonNull [ ] Same as 2a
c. @DynamoDbImmutable with nested @DynamoDbBean as NonNull [ ] Same as 2a
d. @DynamoDbBean with nested @DynamoDbBean as Null [ ] Same as 2a; nested @DynamoDbIgnoreNulls is out of scope
e. @DynamoDbBean with nested @DynamoDbImmutable as Null [ ] Same as 2a; nested @DynamoDbIgnoreNulls is out of scope
f. @DynamoDbImmutable with nested @DynamoDbBean as Null [ ] Same as 2a; nested @DynamoDbIgnoreNulls is out of scope
3. CRUD Operations
a. scan() [x] Used after batch/transact/put-without-key
b. query() [x] PK CREATE and non-key CREATE
c. updateItem() [x] CREATE preserve/generate behavior, all request-level ignore-null modes, and stored-state matrix
d. putItem() [x] Missing and explicit values, including repeated put with the same key
e. getItem() [x]
f. deleteItem() [x]
g. batchGetItem() [x] Including mixed schemas
h. batchWriteItem() [x] Including mixed bean + document
i. transactGetItems() [x] Including mixed schemas
j. transactWriteItems() [x] Including mixed put + update and omitted CREATE preserve/generate behavior
4. Data Types and Null Handling
a. top-level null attributes [x] Explicit mapped NULL generates; omitted value uses if_not_exists
b. collections with null elements [ ] N/A (@DynamoDbAutoGeneratedUuid supports String only)
c. maps with null values [ ] N/A (@DynamoDbAutoGeneratedUuid supports String only)
d. conversion between null Java values and AttributeValue [x] Explicit DynamoDB NULL generates; omitted stored NULL and empty string are preserved
e. full serialization/deserialization cycle with null values [x] Put/get/update round-trip
5. AsyncTable and SyncTable
a. DynamoDbAsyncTable Testing [x] ALWAYS, non-key CREATE, omitted existing/absent states, flatten, and shared client
b. DynamoDbTable Testing [x] Full ignore-null and stored-state matrix
6. New/Modification in Extensions
a. Tables with Scenario in ScenarioSl No.1 (All table schemas are Must) [x] Bean/immutable/static generate; document no-op
b. Test with Default Values in Annotations [x] @DynamoDbAutoGeneratedUuid defaults to ALWAYS
c. Combination of Annotation and Builder passes extension [x] Same client, bean annotation table + static builder table
7. New/Modification in Converters
a. Tables with Scenario in ScenarioSl No.1 (All table schemas are Must) [ ] N/A; this PR does not change converters
b. Test with Default Values in Annotations [ ] N/A
c. Test All Scenarios from 1 to 5 [ ] N/A
8. Shared Client Cross-Table Behavior
a. Single DynamoDbEnhancedClient used by supported schema table and unsupported/custom schema table [x] Bean CREATE + document on one client
b. Extension-enabled shared client: supported schema behavior applied, unsupported schema safely skipped (no throw) [x] Document no-op; bean without annotation unchanged
c. Cache isolation: prior operation on unsupported schema must not impact subsequent supported schema operations [x] Document write then CREATE bean still generates
d. Same scenarios for async shared client [x]
9. Extension Interaction Matrix
a. Default extensions + custom extension chain ordering does not change correctness [x] defaultExtensions + UUID
b. Auto timestamp + versioning interaction on same item [ ] N/A (timestamp not in this PR). UUID + VersionedRecordExtension is covered
c. Auto UUID + update behavior interaction (WRITE_ALWAYS, WRITE_IF_NOT_EXISTS) [x] Sync and async paths; update behavior remains separate from UUID strategy selection
d. Extension behavior with condition expressions (success/failure paths) [x]
e. Extension behavior in batch/transact flows with mixed commands [x] Transact put + update, including omitted CREATE preserve/generate behavior
10. Nested/Flattened Cross-Operation Invariants
a. Flattened attributes preserved across chained operations (put -> update -> scan -> batchGet) [x] CREATE preserved; ALWAYS regenerated on update
b. Flattened map behavior under mixed scalar/map/list payloads [ ] N/A
c. Nested projection + filter combinations (valid + invalid projection names) [ ] N/A
d. Nested structures with tags (partition/sort/index tags) preserved correctly [x] Flattened CREATE/ALWAYS GSI keys
e. Sync + async parity for nested/flattened invariants [x] Async flattened ALWAYS generate/regenerate
11. Mixed-Schema Multi-Table Request
a. batchGet with static + bean + immutable + custom schemas in one request [x] Document used as the unsupported schema
b. batchWrite with supported + unsupported schemas in one request [x] CREATE bean + document
c. transactGet mixed schema tables under extension-enabled client [x] Bean + document
d. transactWrite mixed schema tables with conditions and response options [ ] N/A for response-option matrix; mixed put + update is covered in 9e
e. Validation of partial success/failure semantics and diagnostics [ ] N/A (transactions are all-or-nothing; not UUID-specific)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added a changelog entry. Adding a new entry must be accomplished by running the scripts/new-change script and following the instructions. Commit the new file created by the script in .changes/next-release with your changes.
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

…erated-key-annotation' into feature/define-dynamo-db-autogenerated-key-annotation
…erated-key-annotation' into feature/define-dynamo-db-autogenerated-key-annotation
anasatirbasa and others added 7 commits May 6, 2026 11:01
…-annotation

# Conflicts:
#	services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/extensions/AutoGeneratedUuidExtensionTest.java
…erated-key-annotation' into feature/define-dynamo-db-autogenerated-key-annotation

# Conflicts:
#	services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/annotations/DynamoDbAutoGenerateStrategy.java
#	services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/extensions/annotations/DynamoDbAutoGeneratedUuid.java
@RanVaknin
RanVaknin requested a review from a team as a code owner September 15, 2026 21:20
@RanVaknin RanVaknin closed this Sep 15, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been closed and the conversation has been locked. Comments on closed PRs are hard for our team to see. If you need more assistance, please open a new issue that references this one.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants