Skip to content
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

fix: sandbox client config options #643

Merged
merged 8 commits into from Nov 15, 2023
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']
Comment on lines +99 to +100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a test which asserts that these values are flowing correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 70969c4

);
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`);
}
};
}