diff --git a/.changeset/green-melons-jump.md b/.changeset/green-melons-jump.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/green-melons-jump.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/package-lock.json b/package-lock.json index c3fe532467..af08e8aa72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -616,6 +616,10 @@ "resolved": "packages/backend-secret", "link": true }, + "node_modules/@aws-amplify/backend-seed": { + "resolved": "packages/backend-seed", + "link": true + }, "node_modules/@aws-amplify/backend-storage": { "resolved": "packages/backend-storage", "link": true @@ -19615,9 +19619,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.171.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.171.1.tgz", - "integrity": "sha512-BmXodHmeOWu7EZMwXFA+Mp+SnlZgIwhMxfOmqpdGa5dXF4BWOrs0cm4YgrzcJkg0XK713eXPj5IWGj8YeRIU3g==", + "version": "2.173.2", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.173.2.tgz", + "integrity": "sha512-cL9+z8Pl3VZGoO7BwdsrFAOeud/vSl3at7OvmhihbNprMN15XuFUx/rViAU5OI1m92NbV4NBzYSLbSeCwYLNyw==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -21294,12 +21298,10 @@ } }, "node_modules/constructs": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.2.tgz", - "integrity": "sha512-odjsmhoBKRWa2F/Z3edOSZCb7IgxAL5usXQMRKoINMJzcFfC1GvcbO6Dd/xMGLRv4J/tEsjSLwqLxRfJrjPsQw==", - "engines": { - "node": ">= 18.12.0" - } + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", + "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", + "license": "Apache-2.0" }, "node_modules/content-disposition": { "version": "0.5.4", @@ -32073,6 +32075,49 @@ "@aws-sdk/types": "^3.609.0" } }, + "packages/backend-seed": { + "name": "@aws-amplify/backend-seed", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-secret": "^1.1.5", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" + } + }, + "packages/backend-seed/node_modules/@aws-amplify/platform-core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/platform-core/-/platform-core-1.4.0.tgz", + "integrity": "sha512-MSkgt/A30kXsq93xEJmjlEKVOBA6lOg2pc8PUuCeDfsHEvRagrkZ60pH246MJPb1Ubm/ZwwaeDxT+aUbZ0zohw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-sdk/client-sts": "^3.624.0", + "is-ci": "^3.0.1", + "lodash.mergewith": "^4.6.2", + "lodash.snakecase": "^4.1.1", + "semver": "^7.6.3", + "uuid": "^9.0.1", + "zod": "^3.22.2" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.168.0", + "constructs": "^10.0.0" + } + }, + "packages/backend-seed/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", "version": "1.2.4", diff --git a/packages/backend-seed/.npmignore b/packages/backend-seed/.npmignore new file mode 100644 index 0000000000..dbde1fb5db --- /dev/null +++ b/packages/backend-seed/.npmignore @@ -0,0 +1,14 @@ +# Be very careful editing this file. It is crafted to work around [this issue](https://github.com/npm/npm/issues/4479) + +# First ignore everything +**/* + +# Then add back in transpiled js and ts declaration files +!lib/**/*.js +!lib/**/*.d.ts + +# Then ignore test js and ts declaration files +*.test.js +*.test.d.ts + +# This leaves us with including only js and ts declaration files of functional code diff --git a/packages/backend-seed/API.md b/packages/backend-seed/API.md new file mode 100644 index 0000000000..121684b6ad --- /dev/null +++ b/packages/backend-seed/API.md @@ -0,0 +1,12 @@ +## API Report File for "@aws-amplify/backend-seed" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export const GetSecret: (secretName: string) => Promise; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/backend-seed/README.md b/packages/backend-seed/README.md new file mode 100644 index 0000000000..793417be04 --- /dev/null +++ b/packages/backend-seed/README.md @@ -0,0 +1,3 @@ +# Description + +Replace with a description of this package diff --git a/packages/backend-seed/api-extractor.json b/packages/backend-seed/api-extractor.json new file mode 100644 index 0000000000..0f56de03f6 --- /dev/null +++ b/packages/backend-seed/api-extractor.json @@ -0,0 +1,3 @@ +{ + "extends": "../../api-extractor.base.json" +} diff --git a/packages/backend-seed/package.json b/packages/backend-seed/package.json new file mode 100644 index 0000000000..ced4508f9f --- /dev/null +++ b/packages/backend-seed/package.json @@ -0,0 +1,26 @@ +{ + "name": "@aws-amplify/backend-seed", + "version": "0.1.0", + "type": "module", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": { + "types": "./lib/index.d.ts", + "import": "./lib/index.js", + "require": "./lib/index.js" + } + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "update:api": "api-extractor run --local" + }, + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/backend-secret": "^1.1.5", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" + } +} diff --git a/packages/backend-seed/src/get_secret_seed.ts b/packages/backend-seed/src/get_secret_seed.ts new file mode 100644 index 0000000000..fc462535a6 --- /dev/null +++ b/packages/backend-seed/src/get_secret_seed.ts @@ -0,0 +1,19 @@ +import { getSecretClientWithAmplifyErrorHandling } from '@aws-amplify/backend-secret'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; + +//eslint-disable-next-line jsdoc/require-description +/** + * + */ +export const GetSecret = async (secretName: string): Promise => { + if (!process.env.AMPLIFY_SANDBOX_IDENTIFIER) { + throw new Error('SANDBOX_IDENTIFIER is undefined.'); + } + const backendId: BackendIdentifier = JSON.parse( + process.env.AMPLIFY_SANDBOX_IDENTIFIER + ); + + const secretClient = getSecretClientWithAmplifyErrorHandling(); + const secret = await secretClient.getSecret(backendId, { name: secretName }); + return secret.value; +}; diff --git a/packages/backend-seed/src/index.ts b/packages/backend-seed/src/index.ts new file mode 100644 index 0000000000..e1dc76f915 --- /dev/null +++ b/packages/backend-seed/src/index.ts @@ -0,0 +1,3 @@ +import { GetSecret } from './get_secret_seed.js'; + +export { GetSecret }; diff --git a/packages/backend-seed/src/namespace_resolver.ts b/packages/backend-seed/src/namespace_resolver.ts new file mode 100644 index 0000000000..23bb5019a0 --- /dev/null +++ b/packages/backend-seed/src/namespace_resolver.ts @@ -0,0 +1,35 @@ +import { + AmplifyUserError, + PackageJsonReader, +} from '@aws-amplify/platform-core'; +import { EOL } from 'os'; + +//not using this anymore +//stole this from ../packages/cli/src/backend-identifier/local_namespace_resolver.ts for the POC + +export type NamespaceResolver = { + resolve: () => Promise; +}; + +/** + * Reads the local app name from package.json#name in the current directory + */ +export class LocalNamespaceResolver implements NamespaceResolver { + /** + * packageJsonReader is assigned to an instance member for testing. + * resolve is bound to this so that it can be passed as a function reference + */ + constructor(private readonly packageJsonReader: PackageJsonReader) {} + + /** + * Returns the value of package.json#name from the current working directory + */ + resolve = async () => { + const name = this.packageJsonReader.readFromCwd().name; + if (name) return name; + throw new AmplifyUserError('InvalidPackageJsonError', { + message: 'Cannot load name from the package.json', + resolution: `Ensure you are running ampx commands in root of your project (i.e. in the parent of the 'amplify' directory).${EOL}Also ensure that your root package.json file has a "name" field.`, + }); + }; +} diff --git a/packages/backend-seed/src/sandbox_id_resolver.ts b/packages/backend-seed/src/sandbox_id_resolver.ts new file mode 100644 index 0000000000..71f97a62f6 --- /dev/null +++ b/packages/backend-seed/src/sandbox_id_resolver.ts @@ -0,0 +1,33 @@ +import { userInfo as _userInfo } from 'os'; +import { NamespaceResolver } from './namespace_resolver.js'; +import { BackendIdentifier } from '@aws-amplify/plugin-types'; + +//not using this anymore +//stole this from ../packages/cli/src/commands/sandbox/sandbox_id_resolver.ts for the POC + +/** + * Resolves an ID that can be used to uniquely identify sandbox environments + */ +export class SandboxBackendIdResolver { + /** + * Initialize with an appName resolver + */ + constructor( + private readonly namespaceResolver: NamespaceResolver, + private readonly userInfo = _userInfo + ) {} + + /** + * Returns a concatenation of the resolved appName and the current username + */ + resolve = async (identifier?: string): Promise => { + const namespace = await this.namespaceResolver.resolve(); + const name = identifier || this.userInfo().username; + + return { + namespace, + name, + type: 'sandbox', + }; + }; +} diff --git a/packages/backend-seed/src/types.ts b/packages/backend-seed/src/types.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/backend-seed/tsconfig.json b/packages/backend-seed/tsconfig.json new file mode 100644 index 0000000000..c474c02476 --- /dev/null +++ b/packages/backend-seed/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "rootDir": "src", "outDir": "lib" }, + "references": [ + { "path": "../backend-secret" }, + { "path": "../platform-core" }, + { "path": "../plugin-types" } + ] +} diff --git a/packages/backend-seed/typedoc.json b/packages/backend-seed/typedoc.json new file mode 100644 index 0000000000..35fed2c958 --- /dev/null +++ b/packages/backend-seed/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["src/index.ts"] +} diff --git a/packages/cli/src/commands/sandbox/sandbox-seed/sandbox_seed_command.ts b/packages/cli/src/commands/sandbox/sandbox-seed/sandbox_seed_command.ts new file mode 100644 index 0000000000..ecc3450b73 --- /dev/null +++ b/packages/cli/src/commands/sandbox/sandbox-seed/sandbox_seed_command.ts @@ -0,0 +1,63 @@ +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 { PackageJsonReader } from '@aws-amplify/platform-core'; +import { LocalNamespaceResolver } from '../../../backend-identifier/local_namespace_resolver.js'; + +/** + * + */ +export class SandboxSeedCommand implements CommandModule { + /** + * @inheritDoc + */ + readonly command: string; + + /** + * @inheritDoc + */ + readonly describe: string; + + /** + * Seeds sandbox environment. + */ + constructor() { + this.command = 'seed'; + this.describe = 'Seeds sandbox environment'; + } + + /** + * @inheritDoc + */ + handler = async (): Promise => { + const sandboxID = await new SandboxBackendIdResolver( + new LocalNamespaceResolver(new PackageJsonReader()) + ).resolve(); + + //most of this comes from the initial POC for seed, changed filepath to be more inline with discussions that have happened since then + const seedPath = path.join('seed.ts'); + await execa('tsx', [seedPath], { + cwd: process.cwd(), + stdio: 'inherit', + env: { + AMPLIFY_SANDBOX_IDENTIFIER: JSON.stringify(sandboxID), + }, + }); + }; + + /** + * @inheritDoc + */ + //this section also comes from the initial POC for seed + builder = (yargs: Argv) => { + return yargs.check(() => { + const seedPath = path.join(process.cwd(), 'seed.ts'); + if (!existsSync(seedPath)) { + throw new Error(`${seedPath} must exist`); + } + return true; + }); + }; +} diff --git a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts index ef5454a931..333338d8f9 100644 --- a/packages/cli/src/commands/sandbox/sandbox_command_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_command_factory.ts @@ -10,6 +10,7 @@ import { SandboxBackendIdResolver } from './sandbox_id_resolver.js'; import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_generator_adapter.js'; import { LocalNamespaceResolver } from '../../backend-identifier/local_namespace_resolver.js'; import { createSandboxSecretCommand } from './sandbox-secret/sandbox_secret_command_factory.js'; +import { SandboxSeedCommand } from './sandbox-seed/sandbox_seed_command.js'; import { PackageJsonReader, UsageDataEmitterFactory, @@ -63,7 +64,11 @@ export const createSandboxCommand = (): CommandModule< const commandMiddleWare = new CommandMiddleware(printer); return new SandboxCommand( sandboxFactory, - [new SandboxDeleteCommand(sandboxFactory), createSandboxSecretCommand()], + [ + new SandboxDeleteCommand(sandboxFactory), + createSandboxSecretCommand(), + new SandboxSeedCommand(), + ], clientConfigGeneratorAdapter, commandMiddleWare, eventHandlerFactory.getSandboxEventHandlers