From 3354eb158cb8b515a2888c2e3941dd4fa648c818 Mon Sep 17 00:00:00 2001 From: Dane Pilcher Date: Fri, 12 Jul 2024 17:13:14 -0600 Subject: [PATCH] feat: get datasource map for migration (#2668) --- .../transformer-options-v2.ts | 2 + .../src/__tests__/model-transformer.test.ts | 97 +- .../src/graphql-model-transformer.ts | 23 + .../dynamo-model-resource-generator.ts | 5 +- .../src/resources/model-resource-generator.ts | 4 + .../transform-parameters.ts | 3 + .../API.md | 1 + .../transform-parameters.ts | 3 + .../src/__tests__/graphql-transformer.test.ts | 1 + .../src/__e2e__/utils/index.ts | 1 + packages/graphql-transformer-common/API.md | 1 + .../src/ResourceConstants.ts | 3 + yarn.lock | 1246 +++++++++-------- 13 files changed, 823 insertions(+), 567 deletions(-) diff --git a/packages/amplify-category-api/src/graphql-transformer/transformer-options-v2.ts b/packages/amplify-category-api/src/graphql-transformer/transformer-options-v2.ts index bd4cc5ca77..c9ee94f695 100644 --- a/packages/amplify-category-api/src/graphql-transformer/transformer-options-v2.ts +++ b/packages/amplify-category-api/src/graphql-transformer/transformer-options-v2.ts @@ -291,6 +291,8 @@ const generateTransformParameters = ( allowDestructiveGraphqlSchemaUpdates: false, replaceTableUponGsiUpdate: false, allowGen1Patterns: true, + // TODO: decide name before merging to main + enableGen2Migration: featureFlagProvider.getBoolean('enableGen2Migration'), }; }; diff --git a/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts b/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts index a84fc4d68c..bbdc22cfec 100644 --- a/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts +++ b/packages/amplify-graphql-model-transformer/src/__tests__/model-transformer.test.ts @@ -9,7 +9,7 @@ import { } from '@aws-amplify/graphql-transformer-core'; import { InputObjectTypeDefinitionNode, InputValueDefinitionNode, NamedTypeNode, parse } from 'graphql'; import { getBaseType } from 'graphql-transformer-common'; -import { Template } from 'aws-cdk-lib/assertions'; +import { Template, Match } from 'aws-cdk-lib/assertions'; import { testTransform } from '@aws-amplify/graphql-transformer-test-utils'; import { PrimaryKeyTransformer } from '@aws-amplify/graphql-index-transformer'; import { VpcConfig, ModelDataSourceStrategySqlDbType, SQLLambdaModelDataSourceStrategy } from '@aws-amplify/graphql-transformer-interfaces'; @@ -2316,4 +2316,99 @@ describe('ModelTransformer:', () => { expect(directiveNames).toContain('aws_iam'); }); }); + + describe('migration', () => { + it('should output data source mapping', async () => { + const validSchema = ` + type Post @model { + id: ID! + title: String! + } + `; + + const out = testTransform({ + schema: validSchema, + transformers: [new ModelTransformer()], + transformParameters: { + enableGen2Migration: true, + }, + }); + expect(out).toBeDefined(); + const template = Template.fromJSON(out.rootStack); + template.hasOutput('DataSourceMappingOutput', { + Value: Match.objectLike({ + 'Fn::Join': [ + '', + [ + '{"Post":"', + { + 'Fn::GetAtt': ['Post', 'Outputs.transformerrootstackPostPostTable34CAE87BRef'], + }, + '"}', + ], + ], + }), + Description: 'Mapping of model name to data source table name.', + }); + }); + + it('should set table removal policy to retain', () => { + const validSchema = ` + type Post @model { + id: ID! + title: String! + } + `; + + const out = testTransform({ + schema: validSchema, + transformers: [new ModelTransformer()], + transformParameters: { + enableGen2Migration: true, + }, + }); + expect(out).toBeDefined(); + const postStack = out.stacks['Post']; + const template = Template.fromJSON(postStack); + template.hasResource('AWS::DynamoDB::Table', { + DeletionPolicy: 'Retain', + Properties: { + TableName: { + 'Fn::Join': [ + '', + [ + 'Post-', + { + Ref: 'referencetotransformerrootstackGraphQLAPI20497F53ApiId', + }, + '-', + { + Ref: 'referencetotransformerrootstackenv10C5A902Ref', + }, + ], + ], + }, + }, + }); + }); + + describe('does not add SQL data sources to mapping', () => { + test.each(sqlDatasources)('%s', (dbType) => { + const validSchema = ` + type Post @model { + id: ID! @primaryKey + title: String! + } + `; + + const out = testTransform({ + schema: validSchema, + transformers: [new ModelTransformer(), new PrimaryKeyTransformer()], + dataSourceStrategies: constructDataSourceStrategies(validSchema, makeStrategy(dbType)), + }); + expect(out).toBeDefined(); + expect(out.rootStack.Outputs?.DataSourceMappingOutput).toBeUndefined(); + }); + }); + }); }); diff --git a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts index 2b48fa7238..7e5e57de5d 100644 --- a/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts +++ b/packages/amplify-graphql-model-transformer/src/graphql-model-transformer.ts @@ -34,6 +34,7 @@ import { TransformerTransformSchemaStepContextProvider, TransformerValidationStepContextProvider, DataSourceStrategiesProvider, + DataSourceProvider, } from '@aws-amplify/graphql-transformer-interfaces'; import { ModelDirective } from '@aws-amplify/graphql-directives'; import { ITable } from 'aws-cdk-lib/aws-dynamodb'; @@ -323,9 +324,31 @@ export class ModelTransformer extends TransformerModelBase implements Transforme }; generateResolvers = (context: TransformerContextProvider): void => { + const dataSourceMapping: Record = {}; this.resourceGeneratorMap.forEach((generator) => { generator.generateResources(context); + const ddbDatasources = Object.entries(generator.getDatasourceMap()).filter( + ([, datasource]) => datasource.ds.type === 'AMAZON_DYNAMODB', + ); + ddbDatasources.forEach(([modelName, datasource]) => { + if (datasource.ds.dynamoDbConfig && !cdk.isResolvableObject(datasource.ds.dynamoDbConfig)) { + dataSourceMapping[modelName] = datasource.ds.dynamoDbConfig.tableName; + } + // TODO: probably need a link to docs for this + console.warn( + `Could not resolve table name for ${modelName}. DataSourceMappingOutput is incomplete. Please manually add ${modelName} to the mapping for your migration.`, + ); + }); }); + if (context.transformParameters.enableGen2Migration && context.transformParameters.enableTransformerCfnOutputs) { + const { scope } = context.stackManager; + // TODO: decide final naming before merge to main + new cdk.CfnOutput(cdk.Stack.of(scope), 'DataSourceMappingOutput', { + value: cdk.Stack.of(scope).toJsonString(dataSourceMapping), + description: 'Mapping of model name to data source table name.', + exportName: cdk.Fn.join(':', [cdk.Aws.STACK_NAME, 'DataSourceMappingOutput']), + }); + } }; generateGetResolver = ( diff --git a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts index a348ba6dee..2fd416ea89 100644 --- a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts +++ b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts @@ -68,7 +68,10 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { expression: cdk.Fn.conditionEquals(pointInTimeRecovery, 'true'), }); - const removalPolicy = this.options.EnableDeletionProtection ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY; + const removalPolicy = + this.options.EnableDeletionProtection || context.transformParameters.enableGen2Migration + ? cdk.RemovalPolicy.RETAIN + : cdk.RemovalPolicy.DESTROY; // Expose a way in context to allow proper resource naming const table = new Table(scope, tableLogicalName, { diff --git a/packages/amplify-graphql-model-transformer/src/resources/model-resource-generator.ts b/packages/amplify-graphql-model-transformer/src/resources/model-resource-generator.ts index 6a0ff672ee..4f77d87560 100644 --- a/packages/amplify-graphql-model-transformer/src/resources/model-resource-generator.ts +++ b/packages/amplify-graphql-model-transformer/src/resources/model-resource-generator.ts @@ -730,4 +730,8 @@ export abstract class ModelResourceGenerator { return fields; }; + + getDatasourceMap(): Record { + return this.datasourceMap; + } } diff --git a/packages/amplify-graphql-transformer-core/src/transformer-context/transform-parameters.ts b/packages/amplify-graphql-transformer-core/src/transformer-context/transform-parameters.ts index f40c7ee1fb..1c6b1142da 100644 --- a/packages/amplify-graphql-transformer-core/src/transformer-context/transform-parameters.ts +++ b/packages/amplify-graphql-transformer-core/src/transformer-context/transform-parameters.ts @@ -27,4 +27,7 @@ export const defaultTransformParameters: TransformParameters = { // Search Params enableSearchNodeToNodeEncryption: false, + + // Migration + enableGen2Migration: false, }; diff --git a/packages/amplify-graphql-transformer-interfaces/API.md b/packages/amplify-graphql-transformer-interfaces/API.md index 97195a5c56..58d0eda8f2 100644 --- a/packages/amplify-graphql-transformer-interfaces/API.md +++ b/packages/amplify-graphql-transformer-interfaces/API.md @@ -923,6 +923,7 @@ export type TransformParameters = { enableAutoIndexQueryNames: boolean; respectPrimaryKeyAttributesOnConnectionField: boolean; enableSearchNodeToNodeEncryption: boolean; + enableGen2Migration: boolean; }; // @public (undocumented) diff --git a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transform-parameters.ts b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transform-parameters.ts index 6f82377918..a68f779d62 100644 --- a/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transform-parameters.ts +++ b/packages/amplify-graphql-transformer-interfaces/src/transformer-context/transform-parameters.ts @@ -31,4 +31,7 @@ export type TransformParameters = { // Search Params enableSearchNodeToNodeEncryption: boolean; + + // Migration + enableGen2Migration: boolean; }; diff --git a/packages/amplify-graphql-transformer/src/__tests__/graphql-transformer.test.ts b/packages/amplify-graphql-transformer/src/__tests__/graphql-transformer.test.ts index 9cd5e3c2b6..e08fb789ad 100644 --- a/packages/amplify-graphql-transformer/src/__tests__/graphql-transformer.test.ts +++ b/packages/amplify-graphql-transformer/src/__tests__/graphql-transformer.test.ts @@ -65,6 +65,7 @@ const defaultTransformConfig: TransformConfig = { allowDestructiveGraphqlSchemaUpdates: false, replaceTableUponGsiUpdate: false, allowGen1Patterns: true, + enableGen2Migration: false, }, }; diff --git a/packages/amplify-util-mock/src/__e2e__/utils/index.ts b/packages/amplify-util-mock/src/__e2e__/utils/index.ts index 6d9c3709d3..92a3a4a312 100644 --- a/packages/amplify-util-mock/src/__e2e__/utils/index.ts +++ b/packages/amplify-util-mock/src/__e2e__/utils/index.ts @@ -69,6 +69,7 @@ export const defaultTransformParams: Pick