-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: templategen command e2e integration tests #13993
Changes from 5 commits
8f2ede1
07949c4
d8066c2
423d6a3
b9326c0
b28e101
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import path from 'node:path'; | ||
import assert from 'node:assert'; | ||
import { createNewProjectDir, npmInstall, deleteS3Bucket } from '@aws-amplify/amplify-e2e-core'; | ||
import { assertDefaultGen1Setup } from '../assertions'; | ||
import { setupAndPushDefaultGen1Project, runCodegenCommand, runGen2SandboxCommand, cleanupProjects } from '..'; | ||
import { copyFunctionFile } from '../function_utils'; | ||
import { copyGen1Schema } from '../api_utils'; | ||
import { updatePackageDependency } from '../updatePackageJson'; | ||
import { createS3Bucket } from '../sdk_calls'; | ||
import { runTemplategenCommand, stackRefactor } from '../templategen'; | ||
|
||
void describe('Templategen E2E tests', () => { | ||
void describe('Full Migration Templategen Flow', () => { | ||
let projRoot: string; | ||
let projName: string; | ||
let bucketName: string; | ||
|
||
beforeEach(async () => { | ||
const baseDir = process.env.INIT_CWD ?? process.cwd(); | ||
projRoot = await createNewProjectDir('templategen_e2e_flow_test', path.join(baseDir, '..', '..')); | ||
projName = `test${Math.floor(Math.random() * 1000000)}`; | ||
bucketName = `testbucket${Math.floor(Math.random() * 1000000)}`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing this out! b28e101 |
||
}); | ||
|
||
afterEach(async () => { | ||
await cleanupProjects(projRoot); | ||
await deleteS3Bucket(bucketName); | ||
}); | ||
|
||
void it('should init a project & add auth, function, storage, api with defaults & perform full migration templategen flow', async () => { | ||
await setupAndPushDefaultGen1Project(projRoot, projName); | ||
const { gen1StackName, gen1FunctionName, gen1Region } = await assertDefaultGen1Setup(projRoot); | ||
await createS3Bucket(bucketName, gen1Region); | ||
assert(gen1StackName); | ||
await runCodegenCommand(projRoot); | ||
await copyFunctionFile(projRoot, 'function', gen1FunctionName); | ||
await copyGen1Schema(projRoot, projName); | ||
|
||
// TODO: replace below line with correct package version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this comment still relevant ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Irrelevant and removed - b28e101 |
||
await updatePackageDependency(projRoot, '@aws-amplify/backend'); | ||
|
||
await npmInstall(projRoot); | ||
const gen2StackName = await runGen2SandboxCommand(projRoot); | ||
assert(gen2StackName); | ||
await runTemplategenCommand(projRoot, gen1StackName, gen2StackName); | ||
await stackRefactor(projRoot, 'auth', bucketName); | ||
await stackRefactor(projRoot, 'storage', bucketName); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
type EnvVariableAction = 'SET' | 'DELETE'; | ||
|
||
export function toggleEnvVariable(name: string, option: EnvVariableAction, value?: string) { | ||
if (option === 'SET') { | ||
process.env[name] = value; | ||
} else if (option === 'DELETE') { | ||
delete process.env[name]; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH. this doesn't seem to be providing lot of simplification over calling If we want to build something that takes care of scope of env vars I'd suggest to do something like this.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, but I think even this wouldn't quite work, because there are a few steps which involve setting an env var, and then based on those steps running correctly, the stdout of those commands, gives another envvar which need to be set. So, if the case where I had an env var used for specific steps and then other steps use other vars, this would work well. But for this use case, I think simply going with |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import path from 'path'; | ||
import { RefactorCategory } from './templategen'; | ||
import { getProjectMeta } from '@aws-amplify/amplify-e2e-core'; | ||
import { assertIdentityPool, assertStorage, assertUserPool, assertUserPoolClients } from './assertions'; | ||
import { getResourceDetails } from './sdk_calls'; | ||
|
||
async function getGen1AuthResourceDetails(projRoot: string) { | ||
const gen1ProjRoot = path.join(projRoot, '.amplify', 'migration'); | ||
const gen1Meta = getProjectMeta(gen1ProjRoot); | ||
const gen1Region = gen1Meta.providers.awscloudformation.Region; | ||
const { gen1UserPoolId } = await assertUserPool(gen1Meta, gen1Region); | ||
const { gen1IdentityPoolId } = await assertIdentityPool(gen1Meta, gen1Region); | ||
const { gen1ClientIds } = await assertUserPoolClients(gen1Meta, gen1Region); | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const [gen1ClientIdWeb, gen1ClientId] = gen1ClientIds; | ||
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't do this. If this will be used earlier then this should be added later in follow up PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted. I'll keep this in mind. b28e101 |
||
const gen1ResourceIds = [gen1UserPoolId, gen1IdentityPoolId, gen1ClientIdWeb]; | ||
|
||
const gen1ResourceDetails = await Promise.all([ | ||
getResourceDetails('AWS::Cognito::UserPool', gen1UserPoolId, gen1Region), | ||
getResourceDetails('AWS::Cognito::IdentityPool', gen1IdentityPoolId, gen1Region), | ||
getResourceDetails('AWS::Cognito::UserPoolClient', `${gen1UserPoolId}|${gen1ClientIdWeb}`, gen1Region), | ||
]); | ||
|
||
return { gen1ResourceIds, gen1ResourceDetails }; | ||
} | ||
|
||
async function getGen1StorageResourceDetails(projRoot: string) { | ||
const gen1ProjRoot = path.join(projRoot, '.amplify', 'migration'); | ||
const gen1Meta = getProjectMeta(gen1ProjRoot); | ||
const gen1Region = gen1Meta.providers.awscloudformation.Region; | ||
const { gen1BucketName } = await assertStorage(gen1Meta, gen1Region); | ||
const gen1ResourceIds = [gen1BucketName]; | ||
const gen1ResourceDetails = await getResourceDetails('AWS::S3::Bucket', gen1BucketName, gen1Region); | ||
return { gen1ResourceIds, gen1ResourceDetails }; | ||
} | ||
|
||
export async function getGen1ResourceDetails(projRoot: string, category: RefactorCategory) { | ||
if (category === 'auth') { | ||
return await getGen1AuthResourceDetails(projRoot); | ||
} else { | ||
return await getGen1StorageResourceDetails(projRoot); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { getProjectOutputs } from './projectOutputs'; | ||
import { getResourceDetails } from './sdk_calls'; | ||
import { RefactorCategory } from './templategen'; | ||
|
||
async function getGen2AuthResourceDetails(projRoot: string) { | ||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2Region = gen2Meta.auth.aws_region; | ||
const gen2UserPoolId = gen2Meta.auth.user_pool_id; | ||
const gen2ClientIdWeb = gen2Meta.auth.user_pool_client_id; | ||
const gen2IdentityPoolId = gen2Meta.auth.identity_pool_id; | ||
const gen2ResourceIds = [gen2UserPoolId, gen2IdentityPoolId, gen2ClientIdWeb]; | ||
|
||
const gen2ResourceDetails = await Promise.all([ | ||
getResourceDetails('AWS::Cognito::UserPool', gen2UserPoolId, gen2Region), | ||
getResourceDetails('AWS::Cognito::IdentityPool', gen2IdentityPoolId, gen2Region), | ||
getResourceDetails('AWS::Cognito::UserPoolClient', `${gen2UserPoolId}|${gen2ClientIdWeb}`, gen2Region), | ||
]); | ||
|
||
return { gen2ResourceIds, gen2ResourceDetails }; | ||
} | ||
|
||
async function getGen2StorageResourceDetails(projRoot: string) { | ||
const gen2Meta = getProjectOutputs(projRoot); | ||
const gen2Region = gen2Meta.auth.aws_region; | ||
const gen2BucketName = gen2Meta.storage.bucket_name; | ||
const gen2ResourceIds = [gen2BucketName]; | ||
const gen2ResourceDetails = await getResourceDetails('AWS::S3::Bucket', gen2BucketName, gen2Region); | ||
return { gen2ResourceIds, gen2ResourceDetails }; | ||
} | ||
|
||
export async function getGen2ResourceDetails(projRoot: string, category: RefactorCategory) { | ||
if (category === 'auth') { | ||
return await getGen2AuthResourceDetails(projRoot); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: explicitly check for |
||
return await getGen2StorageResourceDetails(projRoot); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
function extractContent(readmeContent: string, startRegex: string, endRegex: string) { | ||
const pattern = new RegExp(`${startRegex}([\\s\\S]*?)${endRegex}`, 'i'); | ||
const match = readmeContent.match(pattern); | ||
|
||
if (match && match[1]) { | ||
return match[1].trim(); | ||
} | ||
throw new Error('README file parsing failed to get the stack refactor commands'); | ||
} | ||
|
||
function extractCommands(readmeContent: string) { | ||
const pattern = /```([\s\S]*?)```/g; | ||
const matches = readmeContent.matchAll(pattern); | ||
const commands = []; | ||
|
||
for (const match of matches) { | ||
if (match[1]) { | ||
commands.push(match[1].trim()); | ||
} | ||
} | ||
if (commands.length === 0) { | ||
throw new Error('README file parsing failed to get the stack refactor commands'); | ||
} | ||
return commands; | ||
} | ||
|
||
/** | ||
* Sample from the README file for STEP 1: | ||
* | ||
* ### STEP 1: UPDATE GEN-1 AUTH STACK | ||
* | ||
* It is a non-disruptive update since the template only replaces resource references with their resolved values. This is a required step to execute cloudformation stack refactor later. | ||
* | ||
* ``` | ||
* aws cloudformation update-stack \ | ||
* --stack-name my-auth-stack-name \ | ||
* --template-body file://path/to/template.json \ | ||
* --parameters '[{"ParameterKey":"authRoleArn","ParameterValue":"arn:aws:iam::123456789012:role/my-auth-role"},{"ParameterKey":"autoVerifiedAttributes","ParameterValue":"email"},{"ParameterKey":"allowUnauthenticatedIdentities","ParameterValue":"false"},{"ParameterKey":"smsVerificationMessage","ParameterValue":"Your verification code is {####}"}]' \ | ||
* --capabilities CAPABILITY_NAMED_IAM --tags '[]' | ||
* ``` | ||
* | ||
* ``` | ||
* aws cloudformation describe-stacks \ | ||
* --stack-name my-auth-stack-name | ||
* ``` | ||
*/ | ||
export function getCommandsFromReadme(readmeContent: string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Referencing the comment from the previous PR: #13984 (comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: give some sample of the readme fie, like the step 1 portion, would be helpful to visualize the file format. |
||
const step1Content = extractContent(readmeContent, '### STEP 1', '#### Rollback step'); | ||
const step2Content = extractContent(readmeContent, '### STEP 2', '#### Rollback step'); | ||
const step3Content = extractContent(readmeContent, '### STEP 3', '#### Rollback step'); | ||
const step1Commands = extractCommands(step1Content); | ||
const step2commands = extractCommands(step2Content); | ||
const step3Commands = extractCommands(step3Content); | ||
return { step1Commands, step2commands, step3Commands }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are todo comments relevant still ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Irrelevant and removed - b28e101