From f34b1b1286c03d7a98ede1baedc257fcc5447496 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Thu, 12 Dec 2024 14:34:07 -0600 Subject: [PATCH 01/12] fix: Lambda client env var name issue --- .changeset/warm-sloths-tickle.md | 5 + packages/backend-function/API.md | 7 +- .../get_amplify_clients_configuration.ts | 116 ++++++++++++++---- .../amplify/data/resource.ts | 1 + 4 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 .changeset/warm-sloths-tickle.md diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md new file mode 100644 index 0000000000..ad85543a01 --- /dev/null +++ b/.changeset/warm-sloths-tickle.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-function': patch +--- + +Lambda client env var name issue diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index fed504b411..1be1cefa25 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -45,14 +45,11 @@ type DataClientConfig = { // @public (undocumented) type DataClientEnv = { - AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; -}; +} & Record; // @public (undocumented) type DataClientError = { @@ -110,7 +107,7 @@ const getAmplifyDataClientConfig: (env: T, s3Client?: S3Client) => Promise [allow.resource(fcn)])` on the data schema.'; + invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; }; // @public (undocumented) diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index 2f377fe349..8813d18eb3 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -5,37 +5,62 @@ import { S3ServiceException, } from '@aws-sdk/client-s3'; +const dataKeyNameContent = '_MODEL_INTROSPECTION_SCHEMA_KEY'; +const dataBucketNameContent = '_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME'; +const dataEndpointNameContent = '_GRAPHQL_ENDPOINT'; + export type DataClientEnv = { /* eslint-disable @typescript-eslint/naming-convention */ - AMPLIFY_DATA_GRAPHQL_ENDPOINT: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: string; - AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: string; AWS_ACCESS_KEY_ID: string; AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; /* eslint-enable @typescript-eslint/naming-convention */ +} & Record; + +type DataEnvExtension = { + dataBucket: string; + dataKey: string; + dataEndpoint: string; +}; + +type ExtendedAmplifyClientEnv = DataClientEnv & DataEnvExtension; + +const getKeyPrefixesOf = (keys: string[], content: string) => { + return keys + .filter((key) => key.endsWith(content)) + .map((key) => key.slice(0, -1 * content.length)); +}; + +const getDataName = (env: object): string => { + const keys = Object.keys(env); + const bucketNamePrefixes = getKeyPrefixesOf(keys, dataBucketNameContent); + const keyPrefixes = getKeyPrefixesOf(keys, dataKeyNameContent); + const endpointPrefixes = getKeyPrefixesOf(keys, dataEndpointNameContent); + if ( + bucketNamePrefixes.length !== 1 || + keyPrefixes.length !== 1 || + endpointPrefixes.length !== 1 || + bucketNamePrefixes[0] !== keyPrefixes[0] || + bucketNamePrefixes[0] !== endpointPrefixes[0] + ) { + throw new Error( + "The data backend name couldn't be determined. This function may need to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema." + ); + } + return bucketNamePrefixes[0]; }; -const isDataClientEnv = (env: unknown): env is DataClientEnv => { +const isAmplifyClientEnv = (env: object): env is DataClientEnv => { return ( - env !== null && - typeof env === 'object' && - 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME' in env && - 'AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY' in env && 'AWS_ACCESS_KEY_ID' in env && - 'AWS_SECRET_ACCESS_KEY' in env && - 'AWS_SESSION_TOKEN' in env && - 'AWS_REGION' in env && - 'AMPLIFY_DATA_GRAPHQL_ENDPOINT' in env && - typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME === - 'string' && - typeof env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY === 'string' && typeof env.AWS_ACCESS_KEY_ID === 'string' && + 'AWS_SECRET_ACCESS_KEY' in env && typeof env.AWS_SECRET_ACCESS_KEY === 'string' && + 'AWS_SESSION_TOKEN' in env && typeof env.AWS_SESSION_TOKEN === 'string' && - typeof env.AWS_REGION === 'string' && - typeof env.AMPLIFY_DATA_GRAPHQL_ENDPOINT === 'string' + 'AWS_REGION' in env && + typeof env.AWS_REGION === 'string' ); }; @@ -56,16 +81,15 @@ export type ResourceConfig = { /* eslint-enable @typescript-eslint/naming-convention */ const getResourceConfig = ( - env: DataClientEnv, + env: ExtendedAmplifyClientEnv, modelIntrospectionSchema: object ): ResourceConfig => { return { API: { GraphQL: { - endpoint: env.AMPLIFY_DATA_GRAPHQL_ENDPOINT, + endpoint: env.dataEndpoint, region: env.AWS_REGION, defaultAuthMode: 'iam' as const, - modelIntrospection: modelIntrospectionSchema, }, }, @@ -108,7 +132,7 @@ const getLibraryOptions = (env: DataClientEnv): LibraryOptions => { }; export type InvalidConfig = unknown & { - invalidType: 'This function needs to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema.'; + invalidType: 'Some of the AWS environment variables needed to configure Amplify are missing.'; }; export type DataClientError = { @@ -125,6 +149,38 @@ export type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; +const extendEnv = ( + env: DataClientEnv & Record, + dataName: T +): ExtendedAmplifyClientEnv => { + const bucketName = `${dataName}${dataBucketNameContent}`; + const keyName = `${dataName}${dataKeyNameContent}`; + const endpointName = `${dataName}${dataEndpointNameContent}`; + if ( + !( + bucketName in env && + keyName in env && + endpointName in env && + typeof env[bucketName] === 'string' && + typeof env[keyName] === 'string' && + typeof env[endpointName] === 'string' + ) + ) { + throw new Error('The data environment fields are malformed'); + } + + const dataBucket = env[bucketName] as string; + const dataKey = env[keyName] as string; + const dataEndpoint = env[endpointName] as string; + + return { + ...env, + dataBucket, + dataKey, + dataEndpoint, + }; +}; + /** * Generate the `resourceConfig` and `libraryOptions` need to configure * Amplify for the data client in a lambda. @@ -141,17 +197,24 @@ export const getAmplifyDataClientConfig = async ( if (!s3Client) { s3Client = new S3Client(); } + if (env === null || typeof env !== 'object') { + throw new Error(`Invalid environment variables: ${JSON.stringify(env)}`); + } - if (!isDataClientEnv(env)) { + if (!isAmplifyClientEnv(env)) { return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; } + + const dataName = getDataName(env); + const extendedEnv = extendEnv(env, dataName); + let modelIntrospectionSchema: object; try { const response = await s3Client.send( new GetObjectCommand({ - Bucket: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME, - Key: env.AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY, + Bucket: extendedEnv.dataBucket, + Key: extendedEnv.dataKey, }) ); const modelIntrospectionSchemaJson = @@ -173,7 +236,10 @@ export const getAmplifyDataClientConfig = async ( const libraryOptions = getLibraryOptions(env); - const resourceConfig = getResourceConfig(env, modelIntrospectionSchema); + const resourceConfig = getResourceConfig( + extendedEnv, + modelIntrospectionSchema + ); return { resourceConfig, libraryOptions } as DataClientReturn; }; diff --git a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts index 4c3442bdc7..2bb4831790 100644 --- a/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts +++ b/packages/integration-tests/src/test-projects/data_access_from_function/amplify/data/resource.ts @@ -31,6 +31,7 @@ const schema = a export type Schema = ClientSchema; export const data = defineData({ + name: 'DATATEST', schema, authorizationModes: { defaultAuthorizationMode: 'apiKey', From 620073d52cd18c0a979cdf5757bc9adff4954731 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Fri, 13 Dec 2024 16:39:57 -0600 Subject: [PATCH 02/12] Change in env naming including prefix with SSM vars --- package-lock.json | 176 ++++++------- packages/backend-data/src/factory.ts | 15 +- packages/backend-function/API.md | 1 + .../get_amplify_clients_configuration.test.ts | 245 +++++++++++------- .../get_amplify_clients_configuration.ts | 39 +-- packages/backend/package.json | 3 +- ...coped_ssm_environment_entries_generator.ts | 6 +- packages/platform-core/API.md | 3 + packages/platform-core/package.json | 1 + packages/platform-core/src/index.ts | 1 + .../naming_convention_conversions.test.ts | 0 .../src}/naming_convention_conversions.ts | 0 12 files changed, 267 insertions(+), 223 deletions(-) rename packages/{backend/src/engine => platform-core/src}/naming_convention_conversions.test.ts (100%) rename packages/{backend/src/engine => platform-core/src}/naming_convention_conversions.ts (100%) diff --git a/package-lock.json b/package-lock.json index 53ab71e34e..503a3ce89c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31607,18 +31607,18 @@ }, "packages/ai-constructs": { "name": "@aws-amplify/ai-constructs", - "version": "1.0.0", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", "@smithy/types": "^3.3.0", "json-schema-to-ts": "^3.1.1" }, "devDependencies": { - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "typescript": "^5.0.0" }, "peerDependencies": { @@ -31632,12 +31632,12 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.5.0", + "version": "1.5.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/util-arn-parser": "^3.568.0" }, "peerDependencies": { @@ -31647,22 +31647,21 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.8.0", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.4.1", - "@aws-amplify/backend-data": "^1.2.1", - "@aws-amplify/backend-function": "^1.8.0", + "@aws-amplify/backend-auth": "^1.4.2", + "@aws-amplify/backend-data": "^1.2.2", + "@aws-amplify/backend-function": "^1.9.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/backend-storage": "^1.2.3", - "@aws-amplify/client-config": "^1.5.2", + "@aws-amplify/backend-storage": "^1.2.4", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", - "@aws-sdk/client-amplify": "^3.624.0", - "lodash.snakecase": "^4.1.1" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-sdk/client-amplify": "^3.624.0" }, "devDependencies": { "@types/aws-lambda": "^8.10.119", @@ -31676,15 +31675,15 @@ }, "packages/backend-ai": { "name": "@aws-amplify/backend-ai", - "version": "1.0.1", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/ai-constructs": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-schema-types": "^1.2.0", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -31693,17 +31692,17 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.4.1", + "version": "1.4.2", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.5.0", + "@aws-amplify/auth-construct": "^1.5.1", "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-cognito-identity": "^3.624.0", "@aws-sdk/client-cognito-identity-provider": "^3.624.0", "@types/aws-lambda": "^8.10.119", @@ -31716,20 +31715,20 @@ }, "packages/backend-data": { "name": "@aws-amplify/backend-data", - "version": "1.2.1", + "version": "1.2.2", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", + "@aws-amplify/backend-output-storage": "^1.1.4", "@aws-amplify/data-construct": "^1.10.1", "@aws-amplify/data-schema-types": "^1.2.0", "@aws-amplify/graphql-generator": "^0.5.1", - "@aws-amplify/plugin-types": "^1.4.0" + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", "@aws-amplify/data-schema": "^1.13.4", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -31738,11 +31737,11 @@ }, "packages/backend-deployer": { "name": "@aws-amplify/backend-deployer", - "version": "1.1.10", + "version": "1.1.11", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.2.2", - "@aws-amplify/plugin-types": "^1.4.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "strip-ansi": "^6.0.1", "tsx": "^4.6.1" @@ -31872,18 +31871,18 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.8.0", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-s3": "^3.624.0", "execa": "^9.5.1" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1", + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0", "@aws-sdk/client-s3": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", "aws-sdk": "^2.1550.0", @@ -32039,12 +32038,12 @@ }, "packages/backend-output-storage": { "name": "@aws-amplify/backend-output-storage", - "version": "1.1.3", + "version": "1.1.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.0", - "@aws-amplify/platform-core": "^1.0.6", - "@aws-amplify/plugin-types": "^1.3.1" + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0" @@ -32052,10 +32051,10 @@ }, "packages/backend-platform-test-stubs": { "name": "@aws-amplify/backend-platform-test-stubs", - "version": "0.3.6", + "version": "0.3.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/plugin-types": "^1.6.0", "aws-cdk-lib": "^2.168.0", "constructs": "^10.0.0" } @@ -32075,16 +32074,16 @@ }, "packages/backend-storage": { "name": "@aws-amplify/backend-storage", - "version": "1.2.3", + "version": "1.2.4", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.2.1", - "@aws-amplify/backend-output-storage": "^1.1.3", - "@aws-amplify/plugin-types": "^1.5.0" + "@aws-amplify/backend-output-storage": "^1.1.4", + "@aws-amplify/plugin-types": "^1.6.0" }, "devDependencies": { - "@aws-amplify/backend-platform-test-stubs": "^0.3.6", - "@aws-amplify/platform-core": "^1.2.1" + "@aws-amplify/backend-platform-test-stubs": "^0.3.7", + "@aws-amplify/platform-core": "^1.3.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", @@ -32093,21 +32092,21 @@ }, "packages/cli": { "name": "@aws-amplify/backend-cli", - "version": "1.4.2", + "version": "1.4.3", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.9", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/form-generator": "^1.0.3", "@aws-amplify/model-generator": "^1.0.9", - "@aws-amplify/platform-core": "^1.2.0", - "@aws-amplify/plugin-types": "^1.4.0", - "@aws-amplify/sandbox": "^1.2.5", - "@aws-amplify/schema-generator": "^1.2.5", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", + "@aws-amplify/sandbox": "^1.2.7", + "@aws-amplify/schema-generator": "^1.2.6", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-cloudformation": "^3.624.0", "@aws-sdk/client-s3": "^3.624.0", @@ -32140,10 +32139,10 @@ }, "packages/cli-core": { "name": "@aws-amplify/cli-core", - "version": "1.2.0", + "version": "1.2.1", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/platform-core": "^1.0.5", + "@aws-amplify/platform-core": "^1.3.0", "@inquirer/prompts": "^3.0.0", "execa": "^9.5.1", "kleur": "^4.1.5" @@ -32483,14 +32482,14 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.5.2", + "version": "1.5.3", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.4.0", "@aws-amplify/deployed-backend-client": "^1.4.1", "@aws-amplify/model-generator": "^1.0.7", - "@aws-amplify/platform-core": "^1.0.7", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "zod": "^3.22.2" }, "devDependencies": { @@ -32505,12 +32504,12 @@ } }, "packages/create-amplify": { - "version": "1.0.6", + "version": "1.0.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/cli-core": "^1.1.3", - "@aws-amplify/platform-core": "^1.0.3", - "@aws-amplify/plugin-types": "^1.2.2", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "execa": "^9.5.1", "kleur": "^4.1.5", "yargs": "^17.7.2" @@ -32786,20 +32785,20 @@ }, "packages/integration-tests": { "name": "@aws-amplify/integration-tests", - "version": "0.6.0", + "version": "0.6.1", "license": "Apache-2.0", "devDependencies": { "@apollo/client": "^3.10.1", - "@aws-amplify/ai-constructs": "^1.0.0", - "@aws-amplify/auth-construct": "^1.4.0", - "@aws-amplify/backend": "^1.6.0", - "@aws-amplify/backend-ai": "^1.0.0", + "@aws-amplify/ai-constructs": "^1.1.0", + "@aws-amplify/auth-construct": "^1.5.1", + "@aws-amplify/backend": "^1.9.0", + "@aws-amplify/backend-ai": "^1.1.0", "@aws-amplify/backend-secret": "^1.1.4", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.1.0", - "@aws-amplify/plugin-types": "^1.3.1", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-accessanalyzer": "^3.624.0", "@aws-sdk/client-amplify": "^3.624.0", "@aws-sdk/client-bedrock-runtime": "^3.622.0", @@ -32995,13 +32994,14 @@ }, "packages/platform-core": { "name": "@aws-amplify/platform-core", - "version": "1.2.2", + "version": "1.3.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/plugin-types": "^1.5.0", + "@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" @@ -33031,7 +33031,7 @@ }, "packages/plugin-types": { "name": "@aws-amplify/plugin-types", - "version": "1.5.0", + "version": "1.6.0", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/types": "^3.609.0", @@ -33041,16 +33041,16 @@ }, "packages/sandbox": { "name": "@aws-amplify/sandbox", - "version": "1.2.6", + "version": "1.2.7", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-deployer": "^1.1.8", + "@aws-amplify/backend-deployer": "^1.1.11", "@aws-amplify/backend-secret": "^1.1.2", - "@aws-amplify/cli-core": "^1.2.0", - "@aws-amplify/client-config": "^1.5.1", + "@aws-amplify/cli-core": "^1.2.1", + "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/deployed-backend-client": "^1.4.1", - "@aws-amplify/platform-core": "^1.2.1", - "@aws-amplify/plugin-types": "^1.5.0", + "@aws-amplify/platform-core": "^1.3.0", + "@aws-amplify/plugin-types": "^1.6.0", "@aws-sdk/client-cloudwatch-logs": "^3.624.0", "@aws-sdk/client-lambda": "^3.624.0", "@aws-sdk/client-ssm": "^3.624.0", @@ -33072,11 +33072,11 @@ }, "packages/schema-generator": { "name": "@aws-amplify/schema-generator", - "version": "1.2.5", + "version": "1.2.6", "license": "Apache-2.0", "dependencies": { "@aws-amplify/graphql-schema-generator": "^0.11.0", - "@aws-amplify/platform-core": "^1.0.5" + "@aws-amplify/platform-core": "^1.3.0" } } } diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index 98d48b0667..a40b49173a 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -52,6 +52,7 @@ import { Bucket } from 'aws-cdk-lib/aws-s3'; import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'; const modelIntrospectionSchemaKey = 'modelIntrospectionSchema.json'; +const defaultName = 'amplifyData'; /** * Singleton factory for AmplifyGraphqlApi constructs that can be used in Amplify project files. @@ -127,7 +128,7 @@ class DataGenerator implements ConstructContainerEntryGenerator { private readonly getInstanceProps: ConstructFactoryGetInstanceProps, private readonly outputStorageStrategy: BackendOutputStorageStrategy ) { - this.name = props.name ?? 'amplifyData'; + this.name = props.name ?? defaultName; } generateContainerEntry = ({ @@ -307,14 +308,20 @@ class DataGenerator implements ConstructContainerEntryGenerator { convertJsResolverDefinition(scope, amplifyApi, schemasJsFunctions); + const namePrefix = this.name === defaultName ? '' : defaultName; + const ssmEnvironmentEntries = ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({ - [`${this.name}_GRAPHQL_ENDPOINT`]: + [`${namePrefix}${this.name}_GRAPHQL_ENDPOINT`]: amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, - [`${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: modelIntrospectionSchemaBucket.bucketName, - [`${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: modelIntrospectionSchemaKey, + ['AMPLIFY_DATA_DEFAULT_NAME']: `${namePrefix}${this.name}`, + // @deprecated: This backwards compatible name without a prefix will be removed + [`${this.name}_GRAPHQL_ENDPOINT`]: + amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, }); const policyGenerator = new AppSyncPolicyGenerator( diff --git a/packages/backend-function/API.md b/packages/backend-function/API.md index 1be1cefa25..db54778b04 100644 --- a/packages/backend-function/API.md +++ b/packages/backend-function/API.md @@ -49,6 +49,7 @@ type DataClientEnv = { AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; + AMPLIFY_DATA_DEFAULT_NAME: string; } & Record; // @public (undocumented) diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts index e2e5d45407..1287b11922 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.test.ts @@ -4,7 +4,7 @@ import { NoSuchKey, S3, S3ServiceException } from '@aws-sdk/client-s3'; import { getAmplifyDataClientConfig } from './get_amplify_clients_configuration.js'; -const validEnv = { +const validDefaultEnv = { AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME', AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY: @@ -14,6 +14,21 @@ const validEnv = { AWS_SESSION_TOKEN: 'TEST_VALUE for AWS_SESSION_TOKEN', AWS_REGION: 'TEST_VALUE for AWS_REGION', AMPLIFY_DATA_GRAPHQL_ENDPOINT: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + AMPLIFY_DATA_DEFAULT_NAME: 'AmplifyData', +}; + +const validNamedEnv = { + AMPLIFY_DATA_TEST_NAME_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME', + AMPLIFY_DATA_TEST_NAME_MODEL_INTROSPECTION_SCHEMA_KEY: + 'TEST_VALUE for AMPLIFY_DATA_MODEL_INTROSPECTION_SCHEMA_KEY', + AWS_ACCESS_KEY_ID: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + AWS_SECRET_ACCESS_KEY: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + AWS_SESSION_TOKEN: 'TEST_VALUE for AWS_SESSION_TOKEN', + AWS_REGION: 'TEST_VALUE for AWS_REGION', + AMPLIFY_DATA_TEST_NAME_GRAPHQL_ENDPOINT: + 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + AMPLIFY_DATA_DEFAULT_NAME: 'AmplifyDataTestName', }; let mockS3Client: S3; @@ -23,110 +38,146 @@ void describe('getAmplifyDataClientConfig', () => { mockS3Client = new S3(); }); - Object.keys(validEnv).forEach((envFieldToExclude) => { - void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { - const env = { ...validEnv } as Record; - delete env[envFieldToExclude]; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, + [ + { + name: 'no set name', + dataBackendName: 'AMPLIFY_DATA', + validEnv: validDefaultEnv, + }, + { + name: 'an explicit name', + dataBackendName: 'AMPLIFY_DATA_TEST_NAME', + validEnv: validNamedEnv, + }, + ].forEach(({ name, dataBackendName, validEnv }) => { + void describe(`env variable with ${name} for the data backend`, () => { + Object.keys(validEnv) + .filter((k) => k !== 'AMPLIFY_DATA_DEFAULT_NAME') + .forEach((envFieldToExclude) => { + if (envFieldToExclude.includes(dataBackendName)) { + void it(`throws error when ${envFieldToExclude} is not included`, async () => { + const env = { ...validEnv } as Record; + delete env[envFieldToExclude]; + await assert.rejects( + async () => await getAmplifyDataClientConfig(env), + /The data environment variables are malformed/ + ); + }); + + void it(`throws error when ${envFieldToExclude} is not a string`, async () => { + const env = { ...validEnv } as Record; + env[envFieldToExclude] = 123; + await assert.rejects( + async () => await getAmplifyDataClientConfig(env), + /The data environment variables are malformed/ + ); + }); + } else { + void it(`returns empty config objects when ${envFieldToExclude} is not included`, async () => { + const env = { ...validEnv } as Record; + delete env[envFieldToExclude]; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + + void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { + const env = { ...validEnv } as Record; + env[envFieldToExclude] = 123; + assert.deepEqual(await getAmplifyDataClientConfig(env), { + resourceConfig: {}, + libraryOptions: {}, + }); + }); + } + }); + + void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' + ) + ); }); - }); - void it(`returns empty config objects when ${envFieldToExclude} is not a string`, async () => { - const env = { ...validEnv } as Record; - env[envFieldToExclude] = 123; - assert.deepEqual(await getAmplifyDataClientConfig(env), { - resourceConfig: {}, - libraryOptions: {}, + void it('raises a custom error message when there is a S3ServiceException error retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new S3ServiceException({ + name: 'TEST_ERROR', + message: 'TEST_MESSAGE', + $fault: 'server', + $metadata: {}, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error( + 'Error retrieving the schema from S3. You may need to grant this function authorization on the schema. TEST_ERROR: TEST_MESSAGE.' + ) + ); }); - }); - }); - void it('raises a custom error message when the model introspection schema is missing from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new NoSuchKey({ message: 'TEST_ERROR', $metadata: {} }); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error( - 'Error retrieving the schema from S3. Please confirm that your project has a `defineData` included in the `defineBackend` definition.' - ) - ); - }); + void it('re-raises a non-S3 error received when retrieving the model introspection schema from the s3 bucket', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { + throw new Error('Test Error'); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); - void it('raises a custom error message when there is a S3ServiceException error retrieving the model introspection schema from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new S3ServiceException({ - name: 'TEST_ERROR', - message: 'TEST_MESSAGE', - $fault: 'server', - $metadata: {}, + await assert.rejects( + async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), + new Error('Test Error') + ); }); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error( - 'Error retrieving the schema from S3. You may need to grant this function authorization on the schema. TEST_ERROR: TEST_MESSAGE.' - ) - ); - }); - void it('re-raises a non-S3 error received when retrieving the model introspection schema from the s3 bucket', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', async () => { - throw new Error('Test Error'); - }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - await assert.rejects( - async () => await getAmplifyDataClientConfig(validEnv, mockS3Client), - new Error('Test Error') - ); - }); - - void it('returns the expected libraryOptions and resourceConfig values in the happy case', async () => { - const s3ClientSendMock = mock.method(mockS3Client, 'send', () => { - return Promise.resolve({ - Body: { - transformToString: () => JSON.stringify({ testSchema: 'TESTING' }), - }, + void it('returns the expected libraryOptions and resourceConfig values in the happy case', async () => { + const s3ClientSendMock = mock.method(mockS3Client, 'send', () => { + return Promise.resolve({ + Body: { + transformToString: () => + JSON.stringify({ testSchema: 'TESTING' }), + }, + }); + }); + mock.method(mockS3Client, 'send', s3ClientSendMock); + + const { resourceConfig, libraryOptions } = + await getAmplifyDataClientConfig(validEnv, mockS3Client); + + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.getCredentialsAndIdentityId?.(), + { + credentials: { + accessKeyId: 'TEST_VALUE for AWS_ACCESS_KEY_ID', + secretAccessKey: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', + sessionToken: 'TEST_VALUE for AWS_SESSION_TOKEN', + }, + } + ); + assert.deepEqual( + await libraryOptions.Auth.credentialsProvider.clearCredentialsAndIdentityId?.(), + undefined + ); + + assert.deepEqual(resourceConfig, { + API: { + GraphQL: { + endpoint: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', + region: 'TEST_VALUE for AWS_REGION', + defaultAuthMode: 'iam', + modelIntrospection: { testSchema: 'TESTING' }, + }, + }, + }); }); }); - mock.method(mockS3Client, 'send', s3ClientSendMock); - - const { resourceConfig, libraryOptions } = await getAmplifyDataClientConfig( - validEnv, - mockS3Client - ); - - assert.deepEqual( - await libraryOptions.Auth.credentialsProvider.getCredentialsAndIdentityId?.(), - { - credentials: { - accessKeyId: 'TEST_VALUE for AWS_ACCESS_KEY_ID', - secretAccessKey: 'TEST_VALUE for AWS_SECRET_ACCESS_KEY', - sessionToken: 'TEST_VALUE for AWS_SESSION_TOKEN', - }, - } - ); - assert.deepEqual( - await libraryOptions.Auth.credentialsProvider.clearCredentialsAndIdentityId?.(), - undefined - ); - - assert.deepEqual(resourceConfig, { - API: { - GraphQL: { - endpoint: 'TEST_VALUE for AMPLIFY_DATA_GRAPHQL_ENDPOINT', - region: 'TEST_VALUE for AWS_REGION', - defaultAuthMode: 'iam', - modelIntrospection: { testSchema: 'TESTING' }, - }, - }, - }); }); }); diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index 8813d18eb3..cf1c888ed1 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -1,3 +1,4 @@ +import { toScreamingSnakeCase } from '@aws-amplify/platform-core'; import { GetObjectCommand, NoSuchKey, @@ -15,6 +16,7 @@ export type DataClientEnv = { AWS_SECRET_ACCESS_KEY: string; AWS_SESSION_TOKEN: string; AWS_REGION: string; + AMPLIFY_DATA_DEFAULT_NAME: string; /* eslint-enable @typescript-eslint/naming-convention */ } & Record; @@ -26,31 +28,6 @@ type DataEnvExtension = { type ExtendedAmplifyClientEnv = DataClientEnv & DataEnvExtension; -const getKeyPrefixesOf = (keys: string[], content: string) => { - return keys - .filter((key) => key.endsWith(content)) - .map((key) => key.slice(0, -1 * content.length)); -}; - -const getDataName = (env: object): string => { - const keys = Object.keys(env); - const bucketNamePrefixes = getKeyPrefixesOf(keys, dataBucketNameContent); - const keyPrefixes = getKeyPrefixesOf(keys, dataKeyNameContent); - const endpointPrefixes = getKeyPrefixesOf(keys, dataEndpointNameContent); - if ( - bucketNamePrefixes.length !== 1 || - keyPrefixes.length !== 1 || - endpointPrefixes.length !== 1 || - bucketNamePrefixes[0] !== keyPrefixes[0] || - bucketNamePrefixes[0] !== endpointPrefixes[0] - ) { - throw new Error( - "The data backend name couldn't be determined. This function may need to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema." - ); - } - return bucketNamePrefixes[0]; -}; - const isAmplifyClientEnv = (env: object): env is DataClientEnv => { return ( 'AWS_ACCESS_KEY_ID' in env && @@ -60,7 +37,9 @@ const isAmplifyClientEnv = (env: object): env is DataClientEnv => { 'AWS_SESSION_TOKEN' in env && typeof env.AWS_SESSION_TOKEN === 'string' && 'AWS_REGION' in env && - typeof env.AWS_REGION === 'string' + typeof env.AWS_REGION === 'string' && + 'AMPLIFY_DATA_DEFAULT_NAME' in env && + typeof env.AMPLIFY_DATA_DEFAULT_NAME === 'string' ); }; @@ -149,9 +128,9 @@ export type DataClientReturn = T extends DataClientEnv ? DataClientConfig : DataClientError; -const extendEnv = ( +const extendEnv = ( env: DataClientEnv & Record, - dataName: T + dataName: string ): ExtendedAmplifyClientEnv => { const bucketName = `${dataName}${dataBucketNameContent}`; const keyName = `${dataName}${dataKeyNameContent}`; @@ -166,7 +145,7 @@ const extendEnv = ( typeof env[endpointName] === 'string' ) ) { - throw new Error('The data environment fields are malformed'); + throw new Error('The data environment variables are malformed'); } const dataBucket = env[bucketName] as string; @@ -205,7 +184,7 @@ export const getAmplifyDataClientConfig = async ( return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; } - const dataName = getDataName(env); + const dataName = toScreamingSnakeCase(env.AMPLIFY_DATA_DEFAULT_NAME); const extendedEnv = extendEnv(env, dataName); let modelIntrospectionSchema: object; diff --git a/packages/backend/package.json b/packages/backend/package.json index 7ef401f5aa..6ff0d2c384 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -41,8 +41,7 @@ "@aws-amplify/client-config": "^1.5.3", "@aws-amplify/platform-core": "^1.3.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-sdk/client-amplify": "^3.624.0", - "lodash.snakecase": "^4.1.1" + "@aws-sdk/client-amplify": "^3.624.0" }, "peerDependencies": { "aws-cdk-lib": "^2.168.0", diff --git a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts index 067ccf32ba..ba1d9a6d75 100644 --- a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts +++ b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts @@ -1,11 +1,13 @@ -import { ParameterPathConversions } from '@aws-amplify/platform-core'; +import { + ParameterPathConversions, + toScreamingSnakeCase, +} from '@aws-amplify/platform-core'; import { BackendIdentifier, SsmEnvironmentEntriesGenerator, } from '@aws-amplify/plugin-types'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { Construct } from 'constructs'; -import { toScreamingSnakeCase } from './naming_convention_conversions.js'; /** * Generates SsmEnvironmentEntry[] with SSM parameters that are scoped to a specific backend identifier diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index f40d1c5364..09fe3de26c 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -211,6 +211,9 @@ export enum TagName { FRIENDLY_NAME = "amplify:friendly-name" } +// @public +export const toScreamingSnakeCase: (input: string) => string; + // @public export const USAGE_DATA_TRACKING_ENABLED = "telemetry.enabled"; diff --git a/packages/platform-core/package.json b/packages/platform-core/package.json index 6ea68ea197..bce16548a8 100644 --- a/packages/platform-core/package.json +++ b/packages/platform-core/package.json @@ -34,6 +34,7 @@ "is-ci": "^3.0.1", "lodash.mergewith": "^4.6.2", "semver": "^7.6.3", + "lodash.snakecase": "^4.1.1", "uuid": "^9.0.1", "zod": "^3.22.2" }, diff --git a/packages/platform-core/src/index.ts b/packages/platform-core/src/index.ts index 7e05a510ea..97987eade8 100644 --- a/packages/platform-core/src/index.ts +++ b/packages/platform-core/src/index.ts @@ -11,3 +11,4 @@ export { CDKContextKey } from './cdk_context_key.js'; export * from './parameter_path_conversions.js'; export * from './object_accumulator.js'; export { TagName } from './tag_name.js'; +export * from './naming_convention_conversions.js'; diff --git a/packages/backend/src/engine/naming_convention_conversions.test.ts b/packages/platform-core/src/naming_convention_conversions.test.ts similarity index 100% rename from packages/backend/src/engine/naming_convention_conversions.test.ts rename to packages/platform-core/src/naming_convention_conversions.test.ts diff --git a/packages/backend/src/engine/naming_convention_conversions.ts b/packages/platform-core/src/naming_convention_conversions.ts similarity index 100% rename from packages/backend/src/engine/naming_convention_conversions.ts rename to packages/platform-core/src/naming_convention_conversions.ts From db70a7a02fee505315b92adc9d1a40f4e1377bed Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 07:32:26 -0600 Subject: [PATCH 03/12] Reset lockfile --- packages/backend-data/src/factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index a40b49173a..b57a890402 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -319,7 +319,7 @@ class DataGenerator implements ConstructContainerEntryGenerator { [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: modelIntrospectionSchemaKey, ['AMPLIFY_DATA_DEFAULT_NAME']: `${namePrefix}${this.name}`, - // @deprecated: This backwards compatible name without a prefix will be removed + // @deprecated [`${this.name}_GRAPHQL_ENDPOINT`]: amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, }); From c3730b21581fd0d8bddaaba5c0d49e281ca6e7bf Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 07:50:36 -0600 Subject: [PATCH 04/12] Update package lock file --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3fe532467..503a3ce89c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31661,8 +31661,7 @@ "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/platform-core": "^1.3.0", "@aws-amplify/plugin-types": "^1.6.0", - "@aws-sdk/client-amplify": "^3.624.0", - "lodash.snakecase": "^4.1.1" + "@aws-sdk/client-amplify": "^3.624.0" }, "devDependencies": { "@types/aws-lambda": "^8.10.119", @@ -33002,6 +33001,7 @@ "@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" From d30c02c53623b0161e13051efa7b38254eac3b7e Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 08:18:39 -0600 Subject: [PATCH 05/12] Update changeset --- .changeset/warm-sloths-tickle.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md index ad85543a01..a511cf8e8d 100644 --- a/.changeset/warm-sloths-tickle.md +++ b/.changeset/warm-sloths-tickle.md @@ -1,5 +1,7 @@ --- '@aws-amplify/backend-function': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/platform-core': patch --- Lambda client env var name issue From 9ad873551bbdf2c30d8f936cfe4dbe99ffae87dc Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 11:12:40 -0600 Subject: [PATCH 06/12] update name conerter --- .../runtime/get_amplify_clients_configuration.ts | 6 ++++-- ...d_scoped_ssm_environment_entries_generator.ts | 6 ++++-- packages/client-config/API.md | 2 +- packages/platform-core/API.md | 9 ++++++--- .../src/naming_convention_conversions.test.ts | 7 +++++-- .../src/naming_convention_conversions.ts | 16 ++++++++++++---- 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index cf1c888ed1..e9ca248cc5 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -1,4 +1,4 @@ -import { toScreamingSnakeCase } from '@aws-amplify/platform-core'; +import { NamingConverter } from '@aws-amplify/platform-core'; import { GetObjectCommand, NoSuchKey, @@ -184,7 +184,9 @@ export const getAmplifyDataClientConfig = async ( return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn; } - const dataName = toScreamingSnakeCase(env.AMPLIFY_DATA_DEFAULT_NAME); + const dataName = new NamingConverter().toScreamingSnakeCase( + env.AMPLIFY_DATA_DEFAULT_NAME + ); const extendedEnv = extendEnv(env, dataName); let modelIntrospectionSchema: object; diff --git a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts index ba1d9a6d75..c104f96b02 100644 --- a/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts +++ b/packages/backend/src/engine/backend_id_scoped_ssm_environment_entries_generator.ts @@ -1,6 +1,6 @@ import { + NamingConverter, ParameterPathConversions, - toScreamingSnakeCase, } from '@aws-amplify/platform-core'; import { BackendIdentifier, @@ -52,7 +52,9 @@ export class BackendIdScopedSsmEnvironmentEntriesGenerator */ generateSsmEnvironmentEntries = (scopeContext: Record) => Object.entries(scopeContext).map(([contextKey, contextValue]) => { - const sanitizedContextKey = toScreamingSnakeCase(contextKey); + const sanitizedContextKey = new NamingConverter().toScreamingSnakeCase( + contextKey + ); const parameterPath = ParameterPathConversions.toResourceReferenceFullPath( this.backendId, diff --git a/packages/client-config/API.md b/packages/client-config/API.md index cccafacd46..9d784bff52 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -627,7 +627,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 09fe3de26c..fae72441bd 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -145,6 +145,12 @@ class LogRetentionConverter { toCDKRetentionDays: (retention: LogRetention | undefined) => RetentionDays | undefined; } +// @public +export class NamingConverter { + constructor(input: string); + get toScreamingSnakeCase(): string; +} + // @public export class ObjectAccumulator { constructor(accumulator: DeepPartialAmplifyGeneratedConfigs, versionKey?: string); @@ -211,9 +217,6 @@ export enum TagName { FRIENDLY_NAME = "amplify:friendly-name" } -// @public -export const toScreamingSnakeCase: (input: string) => string; - // @public export const USAGE_DATA_TRACKING_ENABLED = "telemetry.enabled"; diff --git a/packages/platform-core/src/naming_convention_conversions.test.ts b/packages/platform-core/src/naming_convention_conversions.test.ts index d6e9e2d496..f2092bc4f3 100644 --- a/packages/platform-core/src/naming_convention_conversions.test.ts +++ b/packages/platform-core/src/naming_convention_conversions.test.ts @@ -1,5 +1,5 @@ import { describe, it } from 'node:test'; -import { toScreamingSnakeCase } from './naming_convention_conversions.js'; +import { NamingConverter } from './naming_convention_conversions.js'; import assert from 'node:assert'; void describe('screaming snake conversions', () => { @@ -16,7 +16,10 @@ void describe('screaming snake conversions', () => { ]; testCases.forEach((testCase) => { void it(`should successfully convert ${testCase.input} to ${testCase.expected}`, () => { - assert.equal(toScreamingSnakeCase(testCase.input), testCase.expected); + assert.equal( + new NamingConverter().toScreamingSnakeCase(testCase.input), + testCase.expected + ); }); }); }); diff --git a/packages/platform-core/src/naming_convention_conversions.ts b/packages/platform-core/src/naming_convention_conversions.ts index 272601da6f..7829db9496 100644 --- a/packages/platform-core/src/naming_convention_conversions.ts +++ b/packages/platform-core/src/naming_convention_conversions.ts @@ -1,8 +1,16 @@ import snakeCase from 'lodash.snakecase'; /** - * Converts input string to SCREAMING_SNAKE_CASE + * Naming Converter + * @example + * new NamingConverter().toScreamingSnakeCase('myInputString') */ -export const toScreamingSnakeCase = (input: string): string => { - return snakeCase(input).toUpperCase(); -}; +export class NamingConverter { + /** + * Converts input string to SCREAMING_SNAKE_CASE + * @param input Input string to convert + */ + public toScreamingSnakeCase(input: string): string { + return snakeCase(input).toUpperCase(); + } +} From 28aeb94ba7892b8bdc85ba6affe581f1aa629504 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 11:20:07 -0600 Subject: [PATCH 07/12] Update api --- packages/platform-core/API.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index fae72441bd..50d40bce8e 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -147,8 +147,7 @@ class LogRetentionConverter { // @public export class NamingConverter { - constructor(input: string); - get toScreamingSnakeCase(): string; + toScreamingSnakeCase(input: string): string; } // @public From a9aa2d31ffa1c485b31f55be756544145d6db848 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 11:59:02 -0600 Subject: [PATCH 08/12] Revert unrelated API change --- packages/client-config/API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-config/API.md b/packages/client-config/API.md index 9d784bff52..cccafacd46 100644 --- a/packages/client-config/API.md +++ b/packages/client-config/API.md @@ -627,7 +627,7 @@ export type CustomClientConfig = { export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion; // @public -export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ +export const generateClientConfig: (backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{ getS3Client: S3Client; getAmplifyClient: AmplifyClient; getCloudFormationClient: CloudFormationClient; From 43f9faf7b06be563d76fe8db7cfa58a539447c5d Mon Sep 17 00:00:00 2001 From: "Aaron S." <94858815+stocaaro@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:53:33 -0600 Subject: [PATCH 09/12] Update .changeset/warm-sloths-tickle.md Co-authored-by: Kamil Sobol --- .changeset/warm-sloths-tickle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md index a511cf8e8d..02a0244464 100644 --- a/.changeset/warm-sloths-tickle.md +++ b/.changeset/warm-sloths-tickle.md @@ -1,7 +1,7 @@ --- -'@aws-amplify/backend-function': patch +'@aws-amplify/backend-function': minor '@aws-amplify/backend-data': patch -'@aws-amplify/platform-core': patch +'@aws-amplify/platform-core': minor --- Lambda client env var name issue From 5afaeff2ef029885bc06093965f99c86ee8b0d86 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 13:57:39 -0600 Subject: [PATCH 10/12] Update changeset description --- .changeset/warm-sloths-tickle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/warm-sloths-tickle.md b/.changeset/warm-sloths-tickle.md index 02a0244464..d880694339 100644 --- a/.changeset/warm-sloths-tickle.md +++ b/.changeset/warm-sloths-tickle.md @@ -4,4 +4,4 @@ '@aws-amplify/platform-core': minor --- -Lambda client env var name issue +Update getAmplifyDataClientConfig to work with named data backend From 1503a10db0a27207516463664e062fd369c7c057 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Mon, 16 Dec 2024 14:33:24 -0600 Subject: [PATCH 11/12] Add env to error message --- .../src/runtime/get_amplify_clients_configuration.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts index e9ca248cc5..8c179fdf57 100644 --- a/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts +++ b/packages/backend-function/src/runtime/get_amplify_clients_configuration.ts @@ -145,7 +145,9 @@ const extendEnv = ( typeof env[endpointName] === 'string' ) ) { - throw new Error('The data environment variables are malformed'); + throw new Error( + `The data environment variables are malformed. env=${JSON.stringify(env)}` + ); } const dataBucket = env[bucketName] as string; From c14ab2c789903a0e24ed22a9043615f0b6844ca8 Mon Sep 17 00:00:00 2001 From: Aaron S Date: Tue, 17 Dec 2024 08:21:01 -0600 Subject: [PATCH 12/12] fix redundant name issue --- packages/backend-data/src/factory.ts | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index b57a890402..af2cfd9789 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -310,18 +310,30 @@ class DataGenerator implements ConstructContainerEntryGenerator { const namePrefix = this.name === defaultName ? '' : defaultName; + const ssmEnvironmentScopeContext = { + [`${namePrefix}${this.name}_GRAPHQL_ENDPOINT`]: + amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: + modelIntrospectionSchemaBucket.bucketName, + [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: + modelIntrospectionSchemaKey, + ['AMPLIFY_DATA_DEFAULT_NAME']: `${namePrefix}${this.name}`, + }; + + const backwardsCompatibleScopeContext = + `${this.name}_GRAPHQL_ENDPOINT` !== + `${namePrefix}${this.name}_GRAPHQL_ENDPOINT` + ? { + // @deprecated + [`${this.name}_GRAPHQL_ENDPOINT`]: + amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + } + : {}; + const ssmEnvironmentEntries = ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({ - [`${namePrefix}${this.name}_GRAPHQL_ENDPOINT`]: - amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, - [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]: - modelIntrospectionSchemaBucket.bucketName, - [`${namePrefix}${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]: - modelIntrospectionSchemaKey, - ['AMPLIFY_DATA_DEFAULT_NAME']: `${namePrefix}${this.name}`, - // @deprecated - [`${this.name}_GRAPHQL_ENDPOINT`]: - amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl, + ...ssmEnvironmentScopeContext, + ...backwardsCompatibleScopeContext, }); const policyGenerator = new AppSyncPolicyGenerator(