From 79ac139975278c462be2a8196a87d47ed4e46f77 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Wed, 15 Nov 2023 11:15:44 -0800 Subject: [PATCH] fix: sandbox client config options (#643) * fix: client config generation respects sandbox config-out-dir option * fix: client config generation respects sandbox format option * rename sandbox format to config-format * make validateDirectory async * add test for client config options * make validateDirectory private --- .changeset/dry-ladybugs-film.md | 5 ++ .changeset/serious-apricots-love.md | 5 ++ .changeset/strong-plums-explain.md | 5 ++ .../commands/sandbox/sandbox_command.test.ts | 86 ++++++++++++++++--- .../src/commands/sandbox/sandbox_command.ts | 53 +++++++----- 5 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 .changeset/dry-ladybugs-film.md create mode 100644 .changeset/serious-apricots-love.md create mode 100644 .changeset/strong-plums-explain.md diff --git a/.changeset/dry-ladybugs-film.md b/.changeset/dry-ladybugs-film.md new file mode 100644 index 0000000000..c9472541f4 --- /dev/null +++ b/.changeset/dry-ladybugs-film.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +client config generation respects sandbox format option diff --git a/.changeset/serious-apricots-love.md b/.changeset/serious-apricots-love.md new file mode 100644 index 0000000000..cbeb3a8b52 --- /dev/null +++ b/.changeset/serious-apricots-love.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +Rename sandbox format to config-format diff --git a/.changeset/strong-plums-explain.md b/.changeset/strong-plums-explain.md new file mode 100644 index 0000000000..ef8dfdc0d0 --- /dev/null +++ b/.changeset/strong-plums-explain.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-cli': patch +--- + +Change sandbox out-dir to config-out-dir and client config generation respects config-out-dir option diff --git a/packages/cli/src/commands/sandbox/sandbox_command.test.ts b/packages/cli/src/commands/sandbox/sandbox_command.test.ts index b48f28eaec..63d8b0dfc7 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.test.ts @@ -7,6 +7,7 @@ import { } from '../../test-utils/command_runner.js'; import assert from 'node:assert'; import fs from 'fs'; +import fsp from 'fs/promises'; import { EventHandler, SandboxCommand } from './sandbox_command.js'; import { createSandboxCommand } from './sandbox_command_factory.js'; import { SandboxDeleteCommand } from './sandbox-delete/sandbox_delete_command.js'; @@ -14,6 +15,7 @@ import { Sandbox, SandboxSingletonFactory } from '@aws-amplify/sandbox'; import { createSandboxSecretCommand } from './sandbox-secret/sandbox_secret_command_factory.js'; import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_generator_adapter.js'; import { CommandMiddleware } from '../../command_middleware.js'; +import path from 'path'; void describe('sandbox command factory', () => { void it('instantiate a sandbox command correctly', () => { @@ -104,8 +106,8 @@ void describe('sandbox command', () => { assert.match(output, /--name/); assert.match(output, /--dir-to-watch/); assert.match(output, /--exclude/); - assert.match(output, /--format/); - assert.match(output, /--out-dir/); + assert.match(output, /--config-format/); + assert.match(output, /--config-out-dir/); assert.equal(mockHandleProfile.mock.callCount(), 0); }); @@ -115,22 +117,32 @@ void describe('sandbox command', () => { }); void it('fails if invalid dir-to-watch is provided', async () => { - const output = await commandRunner.runCommand( - 'sandbox --dir-to-watch nonExistentDir' + const dirToWatchError = new Error( + '--dir-to-watch nonExistentDir does not exist' + ); + mock.method(fsp, 'stat', () => Promise.reject(dirToWatchError)); + await assert.rejects( + () => commandRunner.runCommand('sandbox --dir-to-watch nonExistentDir'), + (err: TestCommandError) => { + assert.equal(err.error.message, dirToWatchError.message); + return true; + } ); - assert.match(output, /--dir-to-watch nonExistentDir does not exist/); }); void it('fails if a file is provided in the --dir-to-watch flag', async (contextual) => { - contextual.mock.method(fs, 'statSync', () => ({ + const dirToWatchError = new Error( + '--dir-to-watch existentFile is not a valid directory' + ); + contextual.mock.method(fsp, 'stat', () => ({ isDirectory: () => false, })); - const output = await commandRunner.runCommand( - 'sandbox --dir-to-watch existentFile' - ); - assert.match( - output, - /--dir-to-watch existentFile is not a valid directory/ + await assert.rejects( + () => commandRunner.runCommand('sandbox --dir-to-watch existentFile'), + (err: TestCommandError) => { + assert.equal(err.error.message, dirToWatchError.message); + return true; + } ); }); @@ -251,4 +263,54 @@ void describe('sandbox command', () => { sandboxProfile ); }); + + void it('fails if invalid config-out-dir is provided', async () => { + const configOutDirError = new Error( + '--config-out-dir nonExistentDir does not exist' + ); + mock.method(fsp, 'stat', () => Promise.reject(configOutDirError)); + await assert.rejects( + () => commandRunner.runCommand('sandbox --config-out-dir nonExistentDir'), + (err: TestCommandError) => { + assert.equal(err.error.message, configOutDirError.message); + return true; + } + ); + }); + + void it('fails if a file is provided for config-out-dir', async (contextual) => { + const configOutDirError = new Error( + '--config-out-dir existentFile is not a valid directory' + ); + contextual.mock.method(fsp, 'stat', () => ({ + isDirectory: () => false, + })); + await assert.rejects( + () => commandRunner.runCommand('sandbox --config-out-dir existentFile'), + (err: TestCommandError) => { + assert.equal(err.error.message, configOutDirError.message); + return true; + } + ); + }); + + void it('starts sandbox with provided client config options as watch exclusions', async (contextual) => { + mock.method(fs, 'lstatSync', () => { + return { + isFile: () => false, + isDir: () => true, + }; + }); + contextual.mock.method(fsp, 'stat', () => ({ + isDirectory: () => true, + })); + await commandRunner.runCommand( + 'sandbox --config-out-dir existentDir --config-format ts' + ); + assert.equal(sandboxStartMock.mock.callCount(), 1); + assert.deepStrictEqual( + sandboxStartMock.mock.calls[0].arguments[0].exclude, + [path.join(process.cwd(), 'existentDir', 'amplifyconfiguration.ts')] + ); + }); }); diff --git a/packages/cli/src/commands/sandbox/sandbox_command.ts b/packages/cli/src/commands/sandbox/sandbox_command.ts index 4db91a8f79..d2fc5183ec 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command.ts @@ -1,5 +1,5 @@ import { Argv, CommandModule } from 'yargs'; -import fs from 'fs'; +import fsp from 'fs/promises'; import { AmplifyPrompter } from '@aws-amplify/cli-core'; import { SandboxSingletonFactory } from '@aws-amplify/sandbox'; import { @@ -19,8 +19,8 @@ type SandboxCommandOptionsCamelCase = { dirToWatch: string | undefined; exclude: string[] | undefined; name: string | undefined; - format: ClientConfigFormat | undefined; - outDir: string | undefined; + configFormat: ClientConfigFormat | undefined; + configOutDir: string | undefined; profile: string | undefined; }; @@ -81,7 +81,9 @@ export class SandboxCommand // attaching event handlers const clientConfigLifecycleHandler = new ClientConfigLifecycleHandler( - this.clientConfigGeneratorAdapter + this.clientConfigGeneratorAdapter, + args['config-out-dir'], + args['config-format'] ); const eventHandlers = this.sandboxEventHandlerCreator?.({ sandboxName: this.sandboxName, @@ -94,8 +96,8 @@ export class SandboxCommand } const watchExclusions = args.exclude ?? []; const clientConfigWritePath = await getClientConfigPath( - args['out-dir'], - args.format + args['config-out-dir'], + args['config-format'] ); watchExclusions.push(clientConfigWritePath); await sandbox.start({ @@ -136,14 +138,14 @@ export class SandboxCommand array: false, global: false, }) - .option('format', { + .option('config-format', { describe: 'Client config output format', type: 'string', array: false, choices: Object.values(ClientConfigFormat), global: false, }) - .option('out-dir', { + .option('config-out-dir', { describe: 'A path to directory where config is written. If not provided defaults to current process working directory.', type: 'string', @@ -155,22 +157,15 @@ export class SandboxCommand type: 'string', array: false, }) - .check((argv) => { + .check(async (argv) => { if (argv['dir-to-watch']) { - // make sure it's a real directory - let stats; - try { - stats = fs.statSync(argv['dir-to-watch'], {}); - } catch (e) { - throw new Error( - `--dir-to-watch ${argv['dir-to-watch']} does not exist` - ); - } - if (!stats.isDirectory()) { - throw new Error( - `--dir-to-watch ${argv['dir-to-watch']} is not a valid directory` - ); - } + await this.validateDirectory('dir-to-watch', argv['dir-to-watch']); + } + if (argv['config-out-dir']) { + await this.validateDirectory( + 'config-out-dir', + argv['config-out-dir'] + ); } if (argv.name) { const projectNameRegex = /^[a-zA-Z0-9-]{1,15}$/; @@ -201,4 +196,16 @@ export class SandboxCommand await this.sandboxFactory.getInstance() ).delete({ name: this.sandboxName }); }; + + private validateDirectory = async (option: string, dir: string) => { + let stats; + try { + stats = await fsp.stat(dir, {}); + } catch (e) { + throw new Error(`--${option} ${dir} does not exist`); + } + if (!stats.isDirectory()) { + throw new Error(`--${option} ${dir} is not a valid directory`); + } + }; }