Skip to content

feat(gen2-migration): geo category for gen2-migration.#14712

Merged
iliapolo merged 76 commits intogen2-migrationfrom
sai/geo-category-gen2-migration
Mar 28, 2026
Merged

feat(gen2-migration): geo category for gen2-migration.#14712
iliapolo merged 76 commits intogen2-migrationfrom
sai/geo-category-gen2-migration

Conversation

@sai-ray
Copy link
Copy Markdown
Contributor

@sai-ray sai-ray commented Mar 28, 2026

Description of changes

Adds geo category support to the gen2-migration tooling: gen2 code generation, sample app, frontend simulation test scripts, and snapshot tests.

Generate step (codegen)

  • Generates amplify/geo/ directory with per-resource CDK constructs for Map, PlaceIndex, and GeofenceCollection
  • Each geo resource gets its own nested stack with geo prefix (e.g., geostoreLocatorGeofence) matching Gen1 Amplify Console naming
  • Top-level geo/resource.ts aggregator wires all geo resources into backend.addOutput() with maps, search indices, and geofence collections config
  • Follows the same pattern as auth/storage/analytics codegen (see feat(gen2-migration): add geo category code generation for gen2-migration  #14596 for the original geo codegen PR)

Refactor step

  • Map and PlaceIndex are stateless so no refactor is needed, skipped as no-ops
  • GeofenceCollection uses Custom::LambdaCallout which CloudFormation StackRefactor does not support. Marked as unsupported in assess() and throws a clear error in forward()/rollback()

Frontend simulation test scripts

  • Added gen1-test-script.ts, gen2-test-script.ts, and test-utils.ts for the store-locator app
  • Tests Location Search (searchByText, searchByCoordinates) and Geofence CRUD (save, get, list, delete) using @aws-amplify/geo APIs
  • Explicitly adds test user to storeLocatorAdmin group via AdminAddUserToGroupCommand after provisioning since AdminCreateUser does not trigger the PostConfirmation Lambda
  • Gen2 script bridges the amplify_outputs.json schema to the Gen1 AmplifyConfig interface for provisionTestUser compatibility
  • Added "type": "module" to _test-common/package.json to fix ESM re-export resolution across all migration apps
  • All 6 tests pass on both Gen1 and Gen2 deployed backends
  • Note: The PostConfirmation Lambda (add-to-group) is not tested because it is a Cognito-internal trigger with no frontend-callable API. The test script uses AdminAddUserToGroupCommand directly to grant group permissions instead. Testing the Lambda would require switching from AdminCreateUser to the SignUp/ConfirmSignUp flow, which diverges from the shared provisionTestUser pattern used by all other apps. For comparison, media-vault's Lambda functions (addUserToGroup, removeUserFromGroup) are exposed as GraphQL resolvers callable from the frontend, so its test script can invoke them directly. Store-locator's Lambda is only invoked internally by Cognito during the self-signup confirmation flow.
  • Note: Map rendering (createMap() from maplibre-gl-js-amplify) is not tested because it requires a DOM container and WebGL context which are unavailable in Node. The map resource is implicitly validated by the search and geofence tests succeeding, since they all share the same auth/identity pool configuration.

Migration app

Snapshot tests

  • store-locator snapshot test passes for the generate step

Issue #, if available

Builds on #14596 (geo codegen) and #14637 (store-locator app).

Resolves #14539

Description of how you validated changes

  • npx jest --testPathPattern='generate.test.ts' --testNamePattern='store-locator' passes
  • Ran amplify gen2-migration refactor --to <gen2-stack> against deployed store-locator app — confirmed GeofenceCollection throws the expected unsupported error, blocking the refactor.
  • Ran npx tsx gen1-test-script.ts in amplify-migration-apps/store-locator/ — all 6 tests pass (searchByText, searchByCoordinates, saveGeofences, getGeofence, listGeofences, deleteGeofences)
  • Ran npx tsx gen2-test-script.ts in amplify-migration-apps/store-locator/ — all 6 tests pass against Gen2 backend

Checklist

  • PR description included
  • yarn test passes
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)
  • New AWS SDK calls or CloudFormation actions have been added to relevant test and service IAM policies
  • Pull request labels are added

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

iliapolo added 30 commits March 17, 2026 11:14
Squash of all work on the migration-plan branch since diverging
from gen2-migration. Includes the assess subcommand for migration
readiness, the refactor command rebuild with category-specific
forward/rollback refactorers, the generate-new infrastructure
with Generator+Renderer pattern, unified validation model,
SpinningLogger UX, and comprehensive unit tests.
---
Prompt: squash all commits after the merge base with
gen2-migration into one and commit
Add a noOpLogger() test helper that creates a real SpinningLogger
in debug mode, then replace all `null as any` logger arguments
across 8 refactor test files with it. This improves type safety
without changing test behavior since the logger methods are never
exercised in these tests.

All 30 tests pass.
---
Prompt: In the refactor test directory there are lot of null as
any being used to pass a spinning logger instance - change it to
actually create a proper logger instance.
Plan.validate() now captures the report field from ValidationResult
and renders a "Failed Validations Report" section before the summary
table. Each failed validation shows its description in red followed
by the report text. Also trims the drift report in _validations.ts.
---
Prompt: The report property in ValidationResult is currently not
used at all. We should use to print the validation report in case
the validation failed.
Replace description-JSON-based auth stack classification with
resource-type detection. The new approach checks for the presence
of an AWS::Cognito::UserPool resource instead of parsing the stack
Description field, which is more reliable.

Also rename fetchStackDescription to fetchStack and descriptionCache
to stackCache for accuracy since the method returns the full Stack
object.
---
Prompt: commit what I did
…apping

Split monolithic auth-forward/rollback/utils into separate files
for Cognito and UserPoolGroups, enabling independent forward and
rollback refactoring per auth sub-resource.

Replace gen1LogicalIds map with abstract targetLogicalId() method
on RollbackCategoryRefactorer, giving each subclass explicit
control over logical ID resolution. Extract match() hook on
ForwardCategoryRefactorer for type-matching customization.

Thread DiscoveredResource through CategoryRefactorer base class
so refactorers can use resource metadata (e.g. resourceName) for
stack discovery instead of relying on shared utility functions.

Minor fixes to migration app docs and sanitize script (trailing
newline normalization).
---
Prompt: commit what I have
…tize

---
Prompt: I reset the changes. just commit.
Thread DiscoveredResource through all resource-backed planners
so each operation carries the resource it belongs to. Plan.describe()
now groups operations under resource headers using the format
"<resourceName> (<category>/<service>)", matching the assessment
display style. Ungrouped operations (scaffolding, validations)
render as a flat list.

Changes:
- Add optional `resource` field to AmplifyMigrationOperation
- Update Plan.describe() to group by resource label
- Thread DiscoveredResource into all generate-side planners
  (Auth, ReferenceAuth, Data, S3, DynamoDB, RestApi, Function,
  AnalyticsKinesis) replacing separate resourceName params
- Tag refactor-side operations via CategoryRefactorer and its
  forward/rollback subclasses (already had this.resource)
- Update all affected test files with DiscoveredResource objects
---
Prompt: in the gen2-migration, i want to make the plan
describe itself by listing the description of each operation
per resource.
Change label format to "category/resourceName (service)", add
cyan color to group headers, remove indentation on grouped items,
and add blank lines between groups for readability.
---
Prompt: i've made changes
…ollback

Group all operations under labeled sections — resource-backed ops
use "Resource: category/name (service)", ungrouped ops fall under
"Project". Descriptions rendered in gray for visual hierarchy.

Add auth:Cognito-UserPool-Groups support in refactor assess and
rollback using AuthUserPoolGroupsRollbackRefactorer.
---
Prompt: I've made more changes. commit
Add NODE_OPTIONS="--max-old-space-size=8192" to the commit
command example and instructions to delete the scratch commit
message file after a successful commit.
---
Prompt: add an instruction in AGENTS.md to delete the commit
file after committing and always increase memory size to
prevent lint failures
…plan

Enrich the refactor plan output with changeset reports and
formatted move tables so operators can review exactly what
each operation will change before executing.

Key changes:
- Auth cognito: explicit client matching (GEN1_WEB_CLIENT ↔
  GEN2_WEB_CLIENT, GEN1_NATIVE_APP_CLIENT ↔
  GEN2_NATIVE_APP_CLIENT) replacing negation-based logic.
  Exported shared constants.
- Auth user pool groups: extracted RESOURCE_TYPES constant,
  use USER_POOL_GROUP_TYPE consistently.
- category-refactorer: added changeset preview via
  CreateChangeSetCommand/DescribeChangeSetCommand, made
  updateSource/updateTarget/buildMoveOperations/beforeMovePlan
  async, enriched plan descriptions with changeset reports
  and move tables.
- forward/rollback-category-refactorer: updated to async
  signatures, added move table formatting to descriptions.
- Removed validateSingleResourcePerCategory from refactor.ts.
- Plan output now uses numbered steps and bold labels.
- New files: changeset-report.ts, template-diff.ts,
  move-table.ts (formatting utilities).
- Test stubs updated for new CFN commands.
---
Prompt: I've made changes - commit what i've done. dont run
tests or anything, just commit.
Use full JSON path (Target.Path) instead of just the
top-level property name so duplicate property names like
RoleMappings are distinguishable. Show before/after values
on separate lines for readability. Use bgGray chalk headers
for operation descriptions. Minor spacing tweaks in plan
output and move table.
---
Prompt: I've made more changes. Commit them. not tests.
…et no-changes detection

Replace hand-rolled box-drawing move table with cli-table3
(CLITable) to match existing patterns. Fix changeset
no-changes detection: a CREATE_COMPLETE changeset with an
empty Changes list is the actual no-changes case, not a
waiter failure. formatChangeSetReport now returns undefined
when there are no changes. Remove debug 'bubu' suffix from
cfn-output-resolver.
---
Prompt: commit
…tor plan

Move changeset creation into the validation lifecycle of
updateSource/updateTarget operations. formatChangeSetReport
returns undefined when no changes are detected. The
validation checks report === undefined (valid) and surfaces
the changeset report on failure. The describe output shows
the report regardless. Removed unused chalk and
formatTemplateDiff imports.
---
Prompt: Commit. Don't run tests yet.
Add CreateChangeSetCommand/DescribeChangeSetCommand mocks to
the CloudFormationMock framework and individual test files
that call plan(). Update tests for API changes: renamed
module paths (auth-forward → auth-cognito-forward), new
abstract targetLogicalId method on RollbackCategoryRefactorer,
async beforeMovePlan, updated error message format, and
Cognito-UserPool-Groups now being supported. Remove dead
auth-utils.test.ts for deleted module.

All 376 gen2-migr
Improve category refactorer resilience for partial failure
recovery and multi-stack auth scenarios:

- Handle empty change-sets gracefully when source/target
  templates match deployed state (partial failure recovery)
- Support reusing existing holding stacks in forward path
  for auth's two-gen1-stack-to-one-gen2-stack mapping
- Consolidate rollback restore-from-holding into a single
  operation instead of three separate ops
- Add logging before stack update/move/refactor operations
- Improve plan step formatting (remove extra blank lines,
  add trailing newline to move table)
- Use clearer descriptions for empty change-set validation
---
Prompt: commit my changes
Move physicalResourceId onto MoveMapping so it is populated once
during buildResourceMappings and carried through the entire
refactor pipeline. This eliminates redundant fetchStackResources
calls in buildMoveOperations and the separate physicalIds/types
maps that were threaded to formatMoveTable.

- buildResourceMappings is now async; forward fetches from
  gen1Env, rollback from gen2Branch.
- buildBlueprint is now async to await buildResourceMappings.
- Deleted move-table.ts; renderMappingTable is now a protected
  method on CategoryRefactorer accepting MoveMapping[].
---
Prompt: in category-refactorer - I want to add the physical
resource id to MoveMapping. Also make formatMoveTable accept
MoveMapping[] and remove the unnecessary maps being passed to
it. Remove move-table.ts and put formatMoveTable into a
protected method inside CategoryRefactorer. Rename
formatMoveTable to renderMappingTable.
…fy error handling

Move all non-mutating work out of execute/describe/validate
callbacks so errors surface during planning before any mutations
run. tryRefactorStack and tryUpdateStack now throw on failure
instead of returning result objects, eliminating boilerplate
checks at every call site. createChangeSetReport now cleans up
its changeset via try/finally. Deleted unused
legacy-custom-resource.ts and template-diff.ts.
---
Prompt: hoist computation out of execute callbacks, make
tryRefactorStack and tryUpdateStack throw on failure,
createChangeSetReport should delete its changeset, remove
legacy-custom-resource.ts and template-diff.ts.
Introduce a Cfn class that centralizes all CloudFormation
operations (update, refactor, createChangeSet, findStack,
deleteStack, renderChangeSet) behind a single client instance.
Replace custom polling with SDK waiters
(waitUntilStackUpdateComplete, waitUntilStackRefactorCreate/
ExecuteComplete, waitUntilStackDeleteComplete). Delete
refactorer.ts (re-export of Planner), holding-stack.ts,
cfn-stack-updater.ts, cfn-stack-refactor-updater.ts,
changeset-report.ts, and snap.ts. Move getHoldingStackName
and HOLDING_STACK_SUFFIX into CategoryRefactorer. Inline
snapshot writing into cfn.ts.
---
Prompt: consolidate 3 CFN operations into a Cfn class,
replace custom polling with SDK waiters, remove
refactorer.ts, holding-stack.ts, snap.ts,
changeset-report.ts, inline snap into cfn.ts, remove
resolveStackName, move ensureOutputDirectory to constructor.
Cfn now accepts a SpinningLogger and logs info messages before
every wait operation (stack update, refactor create/execute,
source/destination verification, stack deletion).
---
Prompt: the cfn class should accept the spinning logger and
log info whenever it is waiting on something.
Split rollback holding stack update into its own operation with
a validation that the changeset only adds the placeholder.
Split forward holding stack deletion into a separate operation.
Remove redundant fetchStackResources calls by deriving physical
IDs from blueprint mappings. Move description/header construction
into describe callbacks and ResourceMapping construction into
execute callbacks. Add Cfn.fetchTemplate method. Remove unused
imports.
---
Prompt: split holding stack operations, add validation,
remove redundant fetches, move descriptions into describe
callbacks, add Cfn.fetchTemplate.
Add stack-level deduplication to prevent duplicate updates when
multiple refactorers target the same stack. Thread targetStackId
through buildResourceMappings for better error messages. Rework
forward beforeMove to incrementally build holding stack templates
by fetching existing state. In rollback, defer template computation
into the execute closure and add duplicate-resource detection.
Remove non-null assertions on StackResource fields.
---
Prompt: commit everything I did. don't run tests.
… noop handling

Add resource-scoped log prefixes to Cfn operations so each
category/resource pair is identifiable in output. Remove
StackFacade caching layer so every call fetches fresh state
from CloudFormation. Introduce buildNoopOperation and suppress
the Implications section when all operations are no-ops. In
rollback, skip resources that already exist in the target
stack instead of throwing. Reduce max wait time from 3600s
to 900s and pre-check destination stack existence to select
the correct waiter.
---
Prompt: commit everything I did. Don't run tests. just
commit.
…actor workflow

RefactorBlueprint now carries only mappings and stack IDs.
Templates are fetched and resolved fresh inside each operation's
execute() closure, so sequential refactorers targeting the same
stack always see current state. This fixes the stale template
bug where the second auth refactorer (user-pool-groups) would
operate on a Gen2 template that the first refactorer (cognito)
had already mutated.

updateSource/updateTarget use plan-time resolved stacks directly
(still fresh since they run before any moves). updateSource now
accepts mappings to determine if a placeholder is needed.
move(), beforeMove() (forward), and afterMove() (rollback) all
re-fetch and re-resolve templates at execution time.
---
Prompt: defer template resolution to execution time in
refactor workflow to fix stale template bug when two Gen1
stacks map to the same Gen2 stack.
…and use SDK ResourceMapping

Cfn.refactor() now accepts ResourceMapping[] directly, fetches
both stack templates, moves resources between them, and handles
the full refactor lifecycle internally. This eliminates template
manipulation from callers entirely.

Replace custom MoveMapping with the SDK's ResourceMapping type
throughout the workflow. Simplify move(), beforeMove() (forward),
and afterMove() (rollback) to just pass resource mappings.
Remove fetchHoldingStackTemplate, isPlaceholderOnlyChangeSet,
and the holding stack changeset validation. Move placeholder
logic into addPlaceHolderIfNeeded() at the top of plan().
Fix symmetricDifference check to compare .size === 0.
---
Prompt: Read what i've done and commit it.
iliapolo and others added 25 commits March 24, 2026 00:19
Update flowcharts and plan() lifecycle to reflect that
beforeMove and afterMove now independently discover resources
from stack templates rather than using blueprint mappings.
---
Prompt: run the PR stage from AGENTS.md.
Add tests for multiple matching targets, empty source, resource
already in target (rollback skip), and empty rollback source.
Remove unused CFNResource import from category-refactorer test.
---
Prompt: add missing buildResourceMappings tests.
…torers

Add targetLogicalId tests for auth-cognito-rollback,
auth-user-pool-groups-rollback, storage-rollback,
storage-dynamo-rollback, and analytics-rollback. Add
GroupName-based match tests for auth-user-pool-groups-forward.
Add forward edge case tests for duplicate targets and
ambiguous same-type matching. Fix bare throw in
auth-cognito-rollback to use AmplifyError. Remove unused
AmplifyError import from storage-dynamo-rollback.
---
Prompt: add targetLogicalId and match tests for all
refactorers, fix bare throw.
Add Resource Mapping section to refactor.md explaining forward
type-based matching with usedTargetIds dedup, rollback
targetLogicalId-based mapping, and all error conditions.
---
Prompt: explain happy and unhappy paths of building resource
mappings in refactor.md.
…foreMove

beforeMove now checks the holding stack template before
building resource mappings. Resources that already exist in
the holding stack are skipped to handle re-execution of
forward after a partial failure. Fix test mock to return
empty template for REVIEW_IN_PROGRESS holding stacks.
---
Prompt: fix REVIEW_IN_PROGRESS holding stack test failure.
…holder remains

After moving resources out of the holding stack during
rollback, check if only the migration placeholder resource
remains. If so, delete the holding stack. This cleanup
happens at execution time since each refactorer moves its
own resources independently.
---
Prompt: commit what I did.
Three test files were broken after merging the gen2-migration
branch:

- category-refactorer.test.ts: fetchSourceStackId now uses
  'storage' + resourceName as the prefix. Changed resourceName
  from 'test' to 'avatars' to match the mock's 'storageavatars'
  logical ID.
- backend.generator.test.ts: Removed ensureStorageStack tests
  since that method was replaced by createDynamoDBStack (which
  already has its own tests).
- dynamodb.generator.test.ts: Constructor now takes a
  DiscoveredResource instead of a string. Updated two call sites
  to pass full DiscoveredResource objects.

All 125 test suites (716 tests) pass.
---
Prompt: I merged code from the gen2-migration branch and now
some cli package tests are failing. run and fix.
Update pre-generate input and regenerate post-generate
snapshots to match current codegen output. Changes
include environment variable ordering in auth resource,
geo resource import ordering, amplify.yml build commands,
and package.json metadata.
---
Prompt: commit this
GeofenceCollection resources use Custom::LambdaCallout which
cannot be moved via CloudFormation StackRefactor. Mark them
as unsupported in assess and throw in forward/rollback.
---
Prompt: mark geofence collection as unsupported for
refactor and throw error
Regenerate lockfile after store-locator pre-generate
package.json dependency changes.
---
Prompt: fix yarn.lock CI failure
@sai-ray sai-ray requested a review from a team as a code owner March 28, 2026 03:15
@iliapolo iliapolo enabled auto-merge (squash) March 28, 2026 13:56
@iliapolo iliapolo merged commit c86f4e5 into gen2-migration Mar 28, 2026
4 checks passed
@iliapolo iliapolo deleted the sai/geo-category-gen2-migration branch March 28, 2026 13:58
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