-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: organise file structure and modify logic to get gen2StackName
- Loading branch information
Sanay Yogesh Shah
committed
Oct 10, 2024
1 parent
f8bf860
commit d310c32
Showing
12 changed files
with
845 additions
and
475 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -265,6 +265,7 @@ | |
"multifactor", | ||
"multipart", | ||
"mutex", | ||
"mygen2app", | ||
"namespace", | ||
"netcoreapp", | ||
"netmask", | ||
|
74 changes: 42 additions & 32 deletions
74
packages/amplify-migration-codegen-e2e/src/__tests__/migration_codegen_e2e.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,51 @@ | ||
import path from 'node:path'; | ||
import assert from 'node:assert'; | ||
import { createNewProjectDir } from '@aws-amplify/amplify-e2e-core'; | ||
import { | ||
cleanupProjects, | ||
setupAndPushGen1Project, | ||
assertGen1Setup, | ||
runCodegenCommand, | ||
runGen2SandboxCommand, | ||
assertUserPoolResource, | ||
assertStorageResource, | ||
assertFunctionResource, | ||
assertDataResource, | ||
copyFunctionFile, | ||
copyGen1Schema, | ||
} from '../helpers'; | ||
import { createGen2Renderer } from '@aws-amplify/amplify-gen2-codegen'; | ||
import { copyFunctionFile } from '../function-utils'; | ||
import { copyGen1Schema } from '../api-utils'; | ||
import { cleanupProjects, setupAndPushGen1Project, runCodegenCommand, runGen2SandboxCommand } from '..'; | ||
import { assertGen1Setup, assertUserPoolResource, assertStorageResource, assertFunctionResource, assertDataResource } from '../assertions'; | ||
|
||
void describe('Migration Codegen E2E tests', () => { | ||
let projRoot: string; | ||
beforeEach(async () => { | ||
const baseDir = process.env.INIT_CWD ?? process.cwd(); | ||
projRoot = await createNewProjectDir('codegen_e2e_flow_test', path.join(baseDir, '..', '..')); | ||
void describe('Codegen E2E tests', () => { | ||
void describe('render pipeline', () => { | ||
void it('renders a project with no parameters', async () => { | ||
const pipeline = createGen2Renderer({ | ||
outputDir: path.join(process.env.INIT_CWD ?? './', 'output'), | ||
auth: { | ||
loginOptions: { | ||
email: true, | ||
}, | ||
}, | ||
}); | ||
await assert.doesNotReject(pipeline.render); | ||
}); | ||
}); | ||
void describe('Full Migration Codegen Flow', () => { | ||
let projRoot: string; | ||
let projName: string; | ||
|
||
afterEach(async () => { | ||
await cleanupProjects(projRoot); | ||
}); | ||
beforeEach(async () => { | ||
const baseDir = process.env.INIT_CWD ?? process.cwd(); | ||
projRoot = await createNewProjectDir('codegen_e2e_flow_test', path.join(baseDir, '..', '..')); | ||
projName = `test${Math.floor(Math.random() * 1000000)}`; | ||
}); | ||
|
||
afterEach(async () => { | ||
await cleanupProjects(projRoot); | ||
}); | ||
|
||
void it('performs full migration codegen flow with backend', async () => { | ||
await setupAndPushGen1Project(projRoot, 'CodegenTest'); | ||
const { gen1UserPoolId, gen1FunctionName, gen1BucketName, gen1GraphQLAPIId, gen1Region } = await assertGen1Setup(projRoot); | ||
await assert.doesNotReject(runCodegenCommand(projRoot), 'Codegen failed'); | ||
await copyFunctionFile(projRoot, gen1FunctionName); | ||
await copyGen1Schema(projRoot); | ||
await assert.doesNotReject(runGen2SandboxCommand(projRoot), 'Gen2 CDK deployment failed'); | ||
await assertUserPoolResource(projRoot, gen1UserPoolId, gen1Region); | ||
await assertStorageResource(projRoot, gen1BucketName, gen1Region); | ||
await assertFunctionResource(projRoot, gen1FunctionName, gen1Region); | ||
await assertDataResource(projRoot, gen1GraphQLAPIId, gen1Region); | ||
void it('should init a project & add auth, function, storage, api with defaults & perform full migration codegen flow', async () => { | ||
await setupAndPushGen1Project(projRoot, projName); | ||
const { gen1UserPoolId, gen1FunctionName, gen1BucketName, gen1GraphqlApiId, gen1Region } = await assertGen1Setup(projRoot); | ||
await assert.doesNotReject(runCodegenCommand(projRoot), 'Codegen failed'); | ||
await copyFunctionFile(projRoot, gen1FunctionName); | ||
await copyGen1Schema(projRoot, projName); | ||
const gen2StackName = await runGen2SandboxCommand(projRoot); | ||
await assertUserPoolResource(projRoot, gen1UserPoolId, gen1Region); | ||
await assertStorageResource(projRoot, gen1BucketName, gen1Region); | ||
await assertFunctionResource(projRoot, gen2StackName, gen1FunctionName, gen1Region); | ||
await assertDataResource(projRoot, gen2StackName, gen1GraphqlApiId, gen1Region); | ||
}); | ||
}); | ||
}); |
19 changes: 0 additions & 19 deletions
19
packages/amplify-migration-codegen-e2e/src/__tests__/render_backend.test.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { getProjectSchema } from '@aws-amplify/amplify-e2e-core'; | ||
import * as fs from 'fs-extra'; | ||
import path from 'node:path'; | ||
|
||
export function copyGen1Schema(projRoot: string, projName: string) { | ||
const gen1Schema = getProjectSchema(path.join(projRoot, '.amplify', 'migration'), projName); | ||
|
||
const dataResourcePath = path.join(projRoot, 'amplify', 'data', 'resource.ts'); | ||
const dataResourceContent = fs.readFileSync(dataResourcePath, 'utf-8'); | ||
|
||
const backendPath = path.join(projRoot, 'amplify', 'backend.ts'); | ||
let backendContent = fs.readFileSync(backendPath, 'utf-8'); | ||
|
||
const schemaRegex = /"TODO: Add your existing graphql schema here"/; | ||
const updatedContent = dataResourceContent.replace(schemaRegex, `\`${gen1Schema.trim()}\``); | ||
|
||
const errorRegex = /throw new Error\("TODO: Add Gen 1 GraphQL schema"\);?\s*/; | ||
const finalContent = updatedContent.replace(errorRegex, ''); | ||
|
||
fs.writeFileSync(dataResourcePath, finalContent, 'utf-8'); | ||
|
||
const linesToAdd = ` | ||
const todoTable = backend.data.resources.cfnResources.additionalCfnResources['Todo']; | ||
todoTable.addOverride('Properties.sseSpecification', { sseEnabled: false }); | ||
`; | ||
|
||
backendContent += linesToAdd; | ||
fs.writeFileSync(backendPath, backendContent, 'utf-8'); | ||
} |
147 changes: 147 additions & 0 deletions
147
packages/amplify-migration-codegen-e2e/src/assertions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { | ||
getProjectMeta, | ||
getUserPool, | ||
checkIfBucketExists, | ||
getFunction, | ||
getAppSyncApi, | ||
describeCloudFormationStack, | ||
} from '@aws-amplify/amplify-e2e-core'; | ||
import { getProjectOutputs } from './projectOutputs'; | ||
import { getAppSyncDataSource, getResourceDetails } from './sdk-calls'; | ||
import { removeProperties } from '.'; | ||
|
||
export async function assertGen1Setup(projRoot: string) { | ||
const gen1Meta = getProjectMeta(projRoot); | ||
const gen1Region = gen1Meta.providers.awscloudformation.Region; | ||
const { UserPoolId: gen1UserPoolId } = Object.keys(gen1Meta.auth).map((key) => gen1Meta.auth[key])[0].output; | ||
const { Arn: gen1FunctionArn, Name: gen1FunctionName } = Object.keys(gen1Meta.function).map((key) => gen1Meta.function[key])[0].output; | ||
const { BucketName: gen1BucketName } = Object.keys(gen1Meta.storage).map((key) => gen1Meta.storage[key])[0].output; | ||
const { | ||
GraphQLAPIIdOutput: gen1GraphqlApiId, | ||
GraphQLAPIEndpointOutput, | ||
GraphQLAPIKeyOutput, | ||
} = Object.keys(gen1Meta.api).map((key) => gen1Meta.api[key])[0].output; | ||
const { graphqlApi } = await getAppSyncApi(gen1GraphqlApiId, gen1Region); | ||
|
||
expect(gen1Region).toBeDefined(); | ||
|
||
const cloudUserPool = await getUserPool(gen1UserPoolId, gen1Region); | ||
expect(cloudUserPool.UserPool).toBeDefined(); | ||
|
||
expect(gen1FunctionArn).toBeDefined(); | ||
expect(gen1FunctionName).toBeDefined(); | ||
const cloudFunction = await getFunction(gen1FunctionName, gen1Region); | ||
expect(cloudFunction.Configuration?.FunctionArn).toEqual(gen1FunctionArn); | ||
|
||
expect(gen1BucketName).toBeDefined(); | ||
const bucketExists = await checkIfBucketExists(gen1BucketName, gen1Region); | ||
expect(bucketExists).toMatchObject({}); | ||
|
||
expect(gen1GraphqlApiId).toBeDefined(); | ||
expect(GraphQLAPIEndpointOutput).toBeDefined(); | ||
expect(GraphQLAPIKeyOutput).toBeDefined(); | ||
|
||
expect(graphqlApi).toBeDefined(); | ||
expect(graphqlApi?.apiId).toEqual(gen1GraphqlApiId); | ||
return { gen1UserPoolId, gen1FunctionName, gen1BucketName, gen1GraphqlApiId, gen1Region }; | ||
} | ||
|
||
export async function assertUserPoolResource(projRoot: string, gen1UserPoolId: string, gen1Region: string) { | ||
const gen1Resource = await getResourceDetails('AWS::Cognito::UserPool', gen1UserPoolId, gen1Region); | ||
removeProperties(gen1Resource, ['ProviderURL', 'ProviderName', 'UserPoolId', 'Arn']); | ||
// TODO: remove below line after EmailMessage, EmailSubject, SmsMessage, SmsVerificationMessage, EmailVerificationMessage, EmailVerificationSubject, AccountRecoverySetting inconsistency is fixed | ||
removeProperties(gen1Resource, [ | ||
'UserPoolTags', | ||
'VerificationMessageTemplate.EmailMessage', | ||
'VerificationMessageTemplate.EmailSubject', | ||
'EmailVerificationSubject', | ||
'AccountRecoverySetting', | ||
'EmailVerificationMessage', | ||
]); | ||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2UserPoolId = gen2Meta.auth.user_pool_id; | ||
const gen2Region = gen2Meta.auth.aws_region; | ||
const gen2Resource = await getResourceDetails('AWS::Cognito::UserPool', gen2UserPoolId, gen2Region); | ||
removeProperties(gen2Resource, ['ProviderURL', 'ProviderName', 'UserPoolId', 'Arn']); | ||
// TODO: remove below line after EmailMessage, EmailSubject, SmsMessage, SmsVerificationMessage, EmailVerificationMessage, EmailVerificationSubject, AccountRecoverySetting inconsistency is fixed | ||
removeProperties(gen2Resource, [ | ||
'UserPoolTags', | ||
'VerificationMessageTemplate.EmailMessage', | ||
'VerificationMessageTemplate.SmsMessage', | ||
'VerificationMessageTemplate.EmailSubject', | ||
'SmsVerificationMessage', | ||
'EmailVerificationSubject', | ||
'AccountRecoverySetting', | ||
'EmailVerificationMessage', | ||
]); | ||
expect(gen2Resource).toEqual(gen1Resource); | ||
} | ||
|
||
export async function assertStorageResource(projRoot: string, gen1BucketName: string, gen1Region: string) { | ||
const gen1Resource = await getResourceDetails('AWS::S3::Bucket', gen1BucketName, gen1Region); | ||
removeProperties(gen1Resource, ['DualStackDomainName', 'DomainName', 'BucketName', 'Arn', 'RegionalDomainName', 'Tags', 'WebsiteURL']); | ||
// TODO: remove below line after CorsConfiguration.CorsRules[0].Id inconsistency is fixed | ||
removeProperties(gen1Resource, ['CorsConfiguration.CorsRules[0].Id']); | ||
|
||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2BucketName = gen2Meta.storage.bucket_name; | ||
const gen2Region = gen2Meta.storage.aws_region; | ||
const gen2Resource = await getResourceDetails('AWS::S3::Bucket', gen2BucketName, gen2Region); | ||
removeProperties(gen2Resource, ['DualStackDomainName', 'DomainName', 'BucketName', 'Arn', 'RegionalDomainName', 'Tags', 'WebsiteURL']); | ||
|
||
expect(gen2Resource).toEqual(gen1Resource); | ||
} | ||
|
||
export async function assertFunctionResource( | ||
projRoot: string, | ||
gen2StackName: string | unknown, | ||
gen1FunctionName: string, | ||
gen1Region: string, | ||
) { | ||
const gen1Resource = await getResourceDetails('AWS::Lambda::Function', gen1FunctionName, gen1Region); | ||
removeProperties(gen1Resource, ['Arn', 'FunctionName', 'LoggingConfig.LogGroup', 'Role']); | ||
// TODO: remove below line after Tags inconsistency is fixed | ||
removeProperties(gen1Resource, ['Tags']); | ||
|
||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2Region = gen2Meta.auth.aws_region; | ||
const outputs = (await describeCloudFormationStack(gen2StackName as string, gen2Region)).Outputs; | ||
const gen2FunctionName = JSON.parse(outputs?.find((output) => output.OutputKey === 'definedFunctions')?.OutputValue ?? '[]')[0]; | ||
const gen2Resource = await getResourceDetails('AWS::Lambda::Function', gen2FunctionName, gen2Region); | ||
removeProperties(gen2Resource, ['Arn', 'FunctionName', 'LoggingConfig.LogGroup', 'Role']); | ||
// TODO: remove below line after Environment.Variables.AMPLIFY_SSM_ENV_CONFIG, Tags inconsistency is fixed | ||
removeProperties(gen2Resource, ['Environment.Variables.AMPLIFY_SSM_ENV_CONFIG', 'Tags']); | ||
|
||
expect(gen2Resource).toEqual(gen1Resource); | ||
} | ||
|
||
export async function assertDataResource(projRoot: string, gen2StackName: string | unknown, gen1GraphqlApiId: string, gen1Region: string) { | ||
const gen1Resource = await getAppSyncApi(gen1GraphqlApiId, gen1Region); | ||
const gen1DataSource = (await getAppSyncDataSource(gen1GraphqlApiId, 'TodoTable', gen1Region)) as Record<string, unknown>; | ||
removeProperties(gen1DataSource, ['dataSourceArn', 'serviceRoleArn']); | ||
removeProperties(gen1Resource.graphqlApi as Record<string, unknown>, ['name', 'apiId', 'arn', 'uris', 'tags', 'dns']); | ||
// TODO: remove below line after authenticationType inconsistency is fixed | ||
removeProperties(gen1Resource.graphqlApi as Record<string, unknown>, ['authenticationType']); | ||
|
||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2Region = gen2Meta.data.aws_region; | ||
const outputs = (await describeCloudFormationStack(gen2StackName as string, gen2Region)).Outputs; | ||
const gen2GraphqlApiId = outputs?.find((output) => output.OutputKey === 'awsAppsyncApiId')?.OutputValue ?? ''; | ||
const gen2Resource = await getAppSyncApi(gen2GraphqlApiId, gen2Region); | ||
const gen2DataSource = (await getAppSyncDataSource(gen2GraphqlApiId, 'TodoTable', gen1Region)) as Record<string, unknown>; | ||
removeProperties(gen2DataSource, ['dataSourceArn', 'serviceRoleArn']); | ||
removeProperties(gen2Resource.graphqlApi as Record<string, unknown>, [ | ||
'name', | ||
'apiId', | ||
'arn', | ||
'uris', | ||
'tags', | ||
'additionalAuthenticationProviders', | ||
'dns', | ||
]); | ||
// TODO: remove below line after authenticationType, userPoolConfig inconsistency is fixed | ||
removeProperties(gen2Resource.graphqlApi as Record<string, undefined>, ['authenticationType', 'userPoolConfig']); | ||
|
||
expect(gen2DataSource).toEqual(gen1DataSource); | ||
expect(gen2Resource).toEqual(gen2Resource); | ||
} |
23 changes: 23 additions & 0 deletions
23
packages/amplify-migration-codegen-e2e/src/function-utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import path from 'node:path'; | ||
import * as fs from 'fs-extra'; | ||
|
||
export function copyFunctionFile(projRoot: string, gen1FunctionName: string) { | ||
const sourcePath = path.join( | ||
projRoot, | ||
'.amplify', | ||
'migration', | ||
'amplify', | ||
'backend', | ||
'function', | ||
gen1FunctionName.split('-')[0], | ||
'src', | ||
'index.js', | ||
); | ||
const destinationPath = path.join(projRoot, 'amplify', 'function', gen1FunctionName.split('-')[0], 'handler.ts'); | ||
const content = fs.readFileSync(sourcePath, 'utf8'); | ||
|
||
// Replace the first occurrence of 'event' with 'event: any' | ||
const modifiedContent = content.replace(/(exports\.handler\s*=\s*async\s*\(\s*)event(\s*\))/, '$1event: any$2'); | ||
|
||
fs.writeFileSync(destinationPath, modifiedContent, 'utf8'); | ||
} |
Oops, something went wrong.