Skip to content

Commit

Permalink
fix: sandbox client config options (#643)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rtpascual committed Nov 15, 2023
1 parent 3cda50c commit 79ac139
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-ladybugs-film.md
@@ -0,0 +1,5 @@
---
'@aws-amplify/backend-cli': patch
---

client config generation respects sandbox format option
5 changes: 5 additions & 0 deletions .changeset/serious-apricots-love.md
@@ -0,0 +1,5 @@
---
'@aws-amplify/backend-cli': patch
---

Rename sandbox format to config-format
5 changes: 5 additions & 0 deletions .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
86 changes: 74 additions & 12 deletions packages/cli/src/commands/sandbox/sandbox_command.test.ts
Expand Up @@ -7,13 +7,15 @@ 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';
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', () => {
Expand Down Expand Up @@ -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);
});

Expand All @@ -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;
}
);
});

Expand Down Expand Up @@ -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')]
);
});
});
53 changes: 30 additions & 23 deletions 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 {
Expand All @@ -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;
};

Expand Down Expand Up @@ -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,
Expand All @@ -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({
Expand Down Expand Up @@ -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',
Expand All @@ -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}$/;
Expand Down Expand Up @@ -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`);
}
};
}

0 comments on commit 79ac139

Please sign in to comment.