Skip to content

fix(graphql-model-transformer): derive imported DynamoDB table key schema from @primaryKey - #3523

Merged
sharonyajain merged 3 commits into
mainfrom
fix/imported-table-primary-key-3489
Aug 26, 2026
Merged

fix(graphql-model-transformer): derive imported DynamoDB table key schema from @primaryKey#3523
sharonyajain merged 3 commits into
mainfrom
fix/imported-table-primary-key-3489

Conversation

@sharonyajain

Copy link
Copy Markdown
Contributor

Problem

When migrating a Gen 1 backend to Gen 2 with migratedAmplifyGen1DynamoDbTableMappings, any model whose schema declares a custom @primaryKey (partition key other than id) fails deployment. The Custom::ImportedAmplifyDynamoDBTable resource is synthesized with an expected key schema of id/HASH, so the TableManager import validation rejects the (correct) live Gen 1 table:

Received response status [FAILED] from custom resource. Message returned:
Imported table properties did not match the expected table properties.

The branch stack then rolls back (ROLLBACK_COMPLETE); because it is a failed CREATE, the stack must be deleted manually before any retry.

Reported in #3489 (surfaced downstream as aws-amplify/amplify-backend#3281).

Why it matters

This blocks Gen 1 → Gen 2 migration for any customer whose data model uses a custom primary key — a common pattern — mid-migration, with a rough manual-cleanup recovery. In a reported 19-model schema, 18 tables (default id keys, several with GSIs) migrated fine; the single model with a custom @primaryKey was the only failure.

Fix (symptom → root cause → change)

  • Symptom: the imported table's expected keySchema/attributeDefinitions are id/HASH regardless of the model's @primaryKey.
  • Root cause: AmplifyDynamoModelResourceGenerator.createModelTable instantiates AmplifyDynamoDBTable with a hardcoded partitionKey: { name: 'id', type: STRING } for every table, including imported ones. For owned tables the @primaryKey transformer (replaceDdbPrimaryKey in the index transformer) corrects the key schema downstream via cfnTable.addPropertyOverride(...). That correction does not reach the imported-table validation path, which consumes the initial construct properties directly (import-table.tsgetExpectedKeySchema returns createTableInput.KeySchema verbatim). GSIs are unaffected because they are already derived from the model.
  • Change: in createModelTable, for imported tables only, derive the partition key — and the sort key (single or composite) — from the model's @primaryKey via getPrimaryKeyFieldNodes, mirroring how GSIs are already derived from the model. Composite sort keys reuse the same ModelCompositeKeySeparator() join as the transformer's getSortKeyName. Owned tables are untouched (still id, corrected downstream exactly as before).

Tests

Added three regression tests in amplify-dynamodb-table-generator.test.ts, each asserting the synthesized Custom::ImportedAmplifyDynamoDBTable properties:

  • custom @primaryKey (partition key ≠ id)keySchema/attributeDefinitions use userAuthenticating, not id.
  • custom @primaryKey with a sort keykeySchema is [HASH, RANGE] on the model fields (this case caught a gap in the first iteration and drove the sort-key handling).
  • imported table without a custom @primaryKey → still id/HASH (no regression).

Verified these tests fail on the base code (imported table synthesizes id/HASH) and pass with the fix — confirmed reproduction, not just static analysis.

Manual verification

  • amplify-dynamodb-table-generator.test.ts: 7/7 pass.
  • Full @aws-amplify/graphql-model-transformer suite: 12 suites / 203 tests / 87 snapshots pass — no snapshot drift (the change is gated on isTableImported).
  • @aws-amplify/graphql-index-transformer suite: 93 tests pass.
  • tsc --noEmit on graphql-model-transformer: clean.

Screenshots

N/A — no user-visible UI change (GraphQL transformer synthesis only).

…hema from @PrimaryKey

Imported Amplify-managed DynamoDB tables (Gen 1 -> Gen 2 migration via
migratedAmplifyGen1DynamoDbTableMappings) hardcoded the table partition key to
`id` when synthesizing the Custom::ImportedAmplifyDynamoDBTable resource. For
owned tables the @PrimaryKey transformer corrects the key schema downstream via
a CloudFormation property override, but that correction does not reach the
TableManager import-validation path, which consumes the initial construct
properties directly.

As a result, any model declaring a custom @PrimaryKey (partition key other than
`id`, and/or a sort key) failed import validation with "Imported table
properties did not match the expected table properties", rolling back the whole
branch stack (a failed CREATE that must then be deleted manually before retry).
GSIs were unaffected because they are already derived from the model.

Fix: in AmplifyDynamoModelResourceGenerator.createModelTable, for imported
tables only, derive the partition key (and single/composite sort key) from the
model's @PrimaryKey via getPrimaryKeyFieldNodes, mirroring how GSIs are derived.
Owned tables are unchanged (still `id`, corrected downstream as before).

Adds regression tests: imported table with a custom @PrimaryKey partition key,
with a sort key, and the default-`id` no-regression case.

Fixes #3489
Related: aws-amplify/amplify-backend#3281
@sharonyajain
sharonyajain requested a review from a team as a code owner August 24, 2026 10:06
@sharonyajain
sharonyajain requested a review from sarayev August 24, 2026 11:28
@sharonyajain sharonyajain self-assigned this Aug 24, 2026

@soberm soberm left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the fix for deriving imported DynamoDB table key schema from @primaryKey. The core fix is correct and safely scoped to the imported-table path — no blocking issues. Leaving a few suggestions (test coverage for composite/numeric/enum keys) and nitpicks (DRY/typing).

…y coverage

Address review feedback on imported-table key-schema derivation:

- Resolve key attribute types with an enum-aware helper (mirrors the index
  transformer's attributeTypeFromType). Enum-backed @PrimaryKey / sort-key
  fields are valid and stored as strings; attributeTypeFromScalar throws on
  non-scalar (enum) types, which would have crashed synth.
- Use ModelResourceIDs.ModelCompositeAttributeName for the composite sort-key
  attribute name instead of an inline join, and the CDK Attribute type.
- Add regression tests for a composite sort key, numeric (Int/Float) keys, and
  an enum-typed primary key.
…a derivation

Address further review feedback:

- Extract the imported-table key-schema derivation into a private
  getImportedTableKeySchema method and a keyAttributeType helper, so
  createModelTable stays readable.
- Add a regression test for the implicit-id fallback (an imported model with no
  id field and no @PrimaryKey still gets an id/HASH key schema).
@sharonyajain

sharonyajain commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the latest review batch in 2b27d4201. Dispositions:

Applied

  • Extracted the key derivation into a private getImportedTableKeySchema method plus a keyAttributeType helper; createModelTable is now a single call. No behavior change.
  • Added derives the implicit id key schema for an imported table whose model declares no id field (a type Note @model { content: String } with no id and no @primaryKey), covering the getImplicitlyDefinedIdField fallback. File now has 11 tests; full graphql-model-transformer suite is 207 tests / 87 snapshots green, tsc clean.

@sharonyajain
sharonyajain requested a review from Simone319 August 26, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants