-
Notifications
You must be signed in to change notification settings - Fork 102
Seed #2546
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
Seed #2546
Changes from 26 commits
e3900e3
8260a2e
8935019
31c6da1
bf92de4
6db8fbc
889e903
1dd9c54
ec798d4
0747b04
a996f26
e1a2bc7
0f1bd95
5dd262d
f548f3f
b5d18f5
47123a9
c55f39e
e9991c4
51f47c7
8ce4880
b34532e
c395733
8b3dc92
c447235
892628b
5485bd6
7c2287c
7828ae0
f98fee9
ef27369
8212b46
9ace2f2
7f9915d
5852d7b
a189d11
446018b
0b5910c
7a7b2f9
c7defa8
587586d
d89dd6c
5949f2e
c9755a7
725ef86
9505ba1
909813c
32a77d9
682b22a
72bc973
9f8c5ab
c05e494
b1dcca9
10d4717
2dd52a5
20cee9f
a0682e0
241d6e6
0f2ae13
9949198
bfea93a
e6de640
da3d96b
47836d8
99d4ff2
3cf5bec
31d458e
8488ca1
a40f891
5941f88
cfe281d
83a15e0
a1ee08d
8ef43dc
252b150
55a5253
07adffb
dbde683
a81efe3
b7decfa
15b5277
2bf0970
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,6 @@ | ||
--- | ||
'@aws-amplify/seed': minor | ||
'@aws-amplify/backend-cli': minor | ||
--- | ||
|
||
adding seed feature |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { after, before, describe, it, mock } from 'node:test'; | ||
import fsp from 'fs/promises'; | ||
import fs from 'fs'; | ||
import * as path from 'path'; | ||
import { SandboxSeedCommand } from './sandbox_seed_command.js'; | ||
import yargs, { CommandModule } from 'yargs'; | ||
import { | ||
TestCommandError, | ||
TestCommandRunner, | ||
} from '../../../test-utils/command_runner.js'; | ||
import { createSandboxSecretCommand } from '../sandbox-secret/sandbox_secret_command_factory.js'; | ||
import { EventHandler, SandboxCommand } from '../sandbox_command.js'; | ||
import { SandboxSingletonFactory } from '@aws-amplify/sandbox'; | ||
import { SandboxDeleteCommand } from '../sandbox-delete/sandbox_delete_command.js'; | ||
import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js'; | ||
import { format, printer } from '@aws-amplify/cli-core'; | ||
import { CommandMiddleware } from '../../../command_middleware.js'; | ||
import { SandboxSeedGeneratePolicyCommand } from './sandbox_seed_policy_command.js'; | ||
import assert from 'node:assert'; | ||
import { BackendIdentifier } from '@aws-amplify/plugin-types'; | ||
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js'; | ||
|
||
const seedFileContents = 'console.log(`seed has been run`);'; | ||
|
||
const testBackendNameSpace = 'testSandboxId'; | ||
const testSandboxName = 'testSandboxName'; | ||
|
||
const testBackendId: BackendIdentifier = { | ||
namespace: testBackendNameSpace, | ||
name: testSandboxName, | ||
type: 'sandbox', | ||
}; | ||
|
||
//const testBackendIdStr = 'testBackendId'; | ||
|
||
void describe('sandbox seed command', () => { | ||
let commandRunner: TestCommandRunner; | ||
|
||
const clientConfigGenerationMock = mock.fn<EventHandler>(); | ||
const clientConfigDeletionMock = mock.fn<EventHandler>(); | ||
|
||
const clientConfigGeneratorAdapterMock = { | ||
generateClientConfigToFile: clientConfigGenerationMock, | ||
} as unknown as ClientConfigGeneratorAdapter; | ||
|
||
const commandMiddleware = new CommandMiddleware(printer); | ||
let amplifySeedDir: string; | ||
let fullPath: string; | ||
|
||
const sandboxIdResolver: SandboxBackendIdResolver = { | ||
resolve: () => Promise.resolve(testBackendId), | ||
} as SandboxBackendIdResolver; | ||
|
||
before(async () => { | ||
const sandboxFactory = new SandboxSingletonFactory( | ||
() => Promise.resolve(testBackendId), | ||
printer, | ||
format | ||
); | ||
|
||
const sandboxSeedCommand = new SandboxSeedCommand(sandboxIdResolver, [ | ||
new SandboxSeedGeneratePolicyCommand(sandboxIdResolver), | ||
]); | ||
|
||
const sandboxCommand = new SandboxCommand( | ||
sandboxFactory, | ||
[ | ||
new SandboxDeleteCommand(sandboxFactory), | ||
createSandboxSecretCommand(), | ||
sandboxSeedCommand, | ||
], | ||
clientConfigGeneratorAdapterMock, | ||
commandMiddleware, | ||
() => ({ | ||
successfulDeployment: [clientConfigGenerationMock], | ||
successfulDeletion: [clientConfigDeletionMock], | ||
failedDeployment: [], | ||
}) | ||
); | ||
const parser = yargs().command(sandboxCommand as unknown as CommandModule); | ||
commandRunner = new TestCommandRunner(parser); | ||
}); | ||
|
||
void describe('seed script exists', () => { | ||
before(async () => { | ||
await fsp.mkdir(path.join(process.cwd(), 'amplify', 'seed'), { | ||
recursive: true, | ||
}); | ||
amplifySeedDir = path.join(process.cwd(), 'amplify'); | ||
fullPath = path.join(process.cwd(), 'amplify', 'seed', 'seed.ts'); | ||
await fsp.writeFile(fullPath, seedFileContents, 'utf8'); | ||
}); | ||
|
||
after(async () => { | ||
await fsp.rm(amplifySeedDir, { recursive: true, force: true }); | ||
if (process.env.AMPLIFY_SANDBOX_IDENTIFIER) { | ||
delete process.env.AMPLIFY_SANDBOX_IDENTIFIER; | ||
} | ||
}); | ||
|
||
void it('runs seed if seed script is found', async () => { | ||
const fsOpenSyncMock = mock.method(fs, 'openSync'); | ||
await commandRunner.runCommand('sandbox seed'); | ||
|
||
assert.equal(fsOpenSyncMock.mock.callCount(), 1); | ||
mock.restoreAll(); | ||
}); | ||
|
||
// TO DO: should have a test to see if backendID is set, that will need to happen while seed command is running | ||
}); | ||
|
||
void describe('seed script does not exist', () => { | ||
before(async () => { | ||
await fsp.mkdir(path.join(process.cwd(), 'amplify', 'seed'), { | ||
recursive: true, | ||
}); | ||
amplifySeedDir = path.join(process.cwd(), 'amplify'); | ||
}); | ||
|
||
after(async () => { | ||
await fsp.rm(amplifySeedDir, { recursive: true, force: true }); | ||
if (process.env.AMPLIFY_SANDBOX_IDENTIFIER) { | ||
delete process.env.AMPLIFY_SANDBOX_IDENTIFIER; | ||
} | ||
}); | ||
|
||
void it('throws error if seed script does not exist', async () => { | ||
await assert.rejects( | ||
() => commandRunner.runCommand('sandbox seed'), | ||
(err: TestCommandError) => { | ||
// file differences between Unix and Windows makes it tricky to add the path | ||
assert.match(err.output, /SeedScriptNotFoundError/); | ||
assert.match(err.output, /There is no file that corresponds to/); | ||
assert.match( | ||
err.output, | ||
/Please make a file that corresponds to (.*) and put your seed logic in it/ | ||
); | ||
return true; | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Argv, CommandModule } from 'yargs'; | ||
import path from 'path'; | ||
import { existsSync } from 'fs'; | ||
import { execa } from 'execa'; | ||
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js'; | ||
import { AmplifyUserError } from '@aws-amplify/platform-core'; | ||
import { SandboxCommandGlobalOptions } from '../option_types.js'; | ||
|
||
/** | ||
* Command that runs seed in sandbox environment | ||
*/ | ||
export class SandboxSeedCommand implements CommandModule<object> { | ||
/** | ||
* @inheritDoc | ||
*/ | ||
readonly command: string; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
readonly describe: string; | ||
|
||
/** | ||
* Seeds sandbox environment. | ||
*/ | ||
constructor( | ||
private readonly backendIDResolver: SandboxBackendIdResolver, | ||
private readonly seedSubCommands: CommandModule[] | ||
) { | ||
this.command = 'seed'; | ||
this.describe = 'Seeds sandbox environment'; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
handler = async (): Promise<void> => { | ||
const backendID = await this.backendIDResolver.resolve(); | ||
const seedPath = path.join('amplify', 'seed', 'seed.ts'); | ||
await execa('tsx', [seedPath], { | ||
cwd: process.cwd(), | ||
stdio: 'inherit', | ||
env: { | ||
AMPLIFY_SANDBOX_IDENTIFIER: JSON.stringify(backendID), | ||
}, | ||
}); | ||
}; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
builder = (yargs: Argv): Argv<SandboxCommandGlobalOptions> => { | ||
return yargs.command(this.seedSubCommands).check(() => { | ||
const seedPath = path.join(process.cwd(), 'amplify', 'seed', 'seed.ts'); | ||
if (!existsSync(seedPath)) { | ||
// this only gets sent to outputs | ||
throw new AmplifyUserError('SeedScriptNotFoundError', { | ||
message: `There is no file that corresponds to ${seedPath}`, | ||
resolution: `Please make a file that corresponds to ${seedPath} and put your seed logic in it`, | ||
}); | ||
} | ||
return true; | ||
}); | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,43 @@ | ||||||
import { Argv, CommandModule } from 'yargs'; | ||||||
import { printer } from '@aws-amplify/cli-core'; | ||||||
import { SandboxBackendIdResolver } from '../sandbox_id_resolver.js'; | ||||||
import { generateSeedPolicyTemplate } from '../../../seed-policy-generation/generate_seed_policy_template.js'; | ||||||
|
||||||
/** | ||||||
* Command that generates policy template with permissions to be able to run seed in sandbox environment | ||||||
*/ | ||||||
export class SandboxSeedGeneratePolicyCommand implements CommandModule<object> { | ||||||
/** | ||||||
* @inheritDoc | ||||||
*/ | ||||||
readonly command: string; | ||||||
|
||||||
/** | ||||||
* @inheritDoc | ||||||
*/ | ||||||
readonly describe: string; | ||||||
|
||||||
/** | ||||||
* Generates policy to run seed, is a subcommand of seed | ||||||
*/ | ||||||
constructor(private readonly backendIdResolver: SandboxBackendIdResolver) { | ||||||
this.command = 'generate-policy'; | ||||||
this.describe = 'Generates policy for seeding based on outputs'; | ||||||
|
this.describe = 'Generates policy for seeding based on outputs'; | |
this.describe = 'Generates policy for seeding'; |
That is implementation details, we can avoid disclosing it.
Outdated
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.
nit: we use 2 space indentation
printer.print(JSON.stringify(policyDocument.toJSON(), null, 1)); | |
printer.print(JSON.stringify(policyDocument.toJSON(), null, 2)); |
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.
I'd call it
AMPLIFY_BACKEND_IDENTIFIER
. This will future proof it in case we ever implement branch support in some form.