From 16e96d76108c5ab3eb961658952494c21fde536b Mon Sep 17 00:00:00 2001 From: vigy02 Date: Mon, 9 Dec 2024 12:39:55 -0800 Subject: [PATCH 01/14] Added error mapping for app name not found fault --- .changeset/tasty-rivers-double.md | 5 +++++ .../src/cdk_error_mapper.test.ts | 17 +++++++++++++++++ .../backend-deployer/src/cdk_error_mapper.ts | 17 ++++++++++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .changeset/tasty-rivers-double.md diff --git a/.changeset/tasty-rivers-double.md b/.changeset/tasty-rivers-double.md new file mode 100644 index 0000000000..f86f6d3a8c --- /dev/null +++ b/.changeset/tasty-rivers-double.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-deployer': patch +--- + +Added error mapping for app name not available in the region diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 8860260173..38568614cf 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -659,6 +659,23 @@ npm error enoent`, errorName: 'LambdaEmptyZipFault', expectedDownstreamErrorMessage: undefined, }, + { + errorMessage: 'Error: No apps found with name my-app in region us-west-2', + expectedTopLevelErrorMessage: + 'No Amplify app found with the specified name in the given region.', + errorName: 'AmplifyAppNotFoundError', + expectedDownstreamErrorMessage: + 'Error: No apps found with name my-app in region us-west-2', + }, + { + errorMessage: + 'Error: No apps found with name @packages/backend in region ap-northeast-1', + expectedTopLevelErrorMessage: + 'No Amplify app found with the specified name in the given region.', + errorName: 'AmplifyAppNotFoundError', + expectedDownstreamErrorMessage: + 'Error: No apps found with name @packages/backend in region ap-northeast-1', + }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 4418fc3275..16ca85ef2d 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -497,6 +497,20 @@ export class CdkErrorMapper { errorName: 'CloudFormationDeploymentError', classification: 'ERROR', }, + //This type of error often occurs when: + // Users have not deployed their app to the specified region. + // There's a mismatch between the app name in the local configuration and the deployed app name. + // Users are working in a different AWS account or region than where the app is deployed. + { + errorRegex: + /No apps found with name (?.*) in region (?.*)/, + humanReadableErrorMessage: + 'No Amplify app found with the specified name in the given region.', + resolutionMessage: + 'Ensure that an Amplify app named "{appName}" exists in the "{region}" region.', + errorName: 'AmplifyAppNotFoundError', + classification: 'ERROR', + }, ]; } @@ -528,4 +542,5 @@ export type CDKDeploymentError = | 'SyntaxError' | 'GetLambdaLayerVersionError' | 'LambdaEmptyZipFault' - | 'LambdaMaxSizeExceededError'; + | 'LambdaMaxSizeExceededError' + | 'AmplifyAppNotFoundError'; From f0e0bbeb0bf3bb8fe933b2fc6b24ce665a15f283 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Mon, 9 Dec 2024 13:58:44 -0800 Subject: [PATCH 02/14] Updated the test case for this fault --- packages/backend-deployer/src/cdk_error_mapper.test.ts | 9 --------- packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 38568614cf..19c4dd494e 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -667,15 +667,6 @@ npm error enoent`, expectedDownstreamErrorMessage: 'Error: No apps found with name my-app in region us-west-2', }, - { - errorMessage: - 'Error: No apps found with name @packages/backend in region ap-northeast-1', - expectedTopLevelErrorMessage: - 'No Amplify app found with the specified name in the given region.', - errorName: 'AmplifyAppNotFoundError', - expectedDownstreamErrorMessage: - 'Error: No apps found with name @packages/backend in region ap-northeast-1', - }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 16ca85ef2d..21b50d7132 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -497,7 +497,7 @@ export class CdkErrorMapper { errorName: 'CloudFormationDeploymentError', classification: 'ERROR', }, - //This type of error often occurs when: + // This type of error often occurs when: // Users have not deployed their app to the specified region. // There's a mismatch between the app name in the local configuration and the deployed app name. // Users are working in a different AWS account or region than where the app is deployed. From 645e1e0d6c776200eb816e4db60889d1cce94245 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Mon, 9 Dec 2024 14:04:20 -0800 Subject: [PATCH 03/14] Updated the mapper and the test case to catch the AppNotFound Error --- packages/backend-deployer/src/cdk_error_mapper.test.ts | 5 ++--- packages/backend-deployer/src/cdk_error_mapper.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 19c4dd494e..80406f8822 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -662,10 +662,9 @@ npm error enoent`, { errorMessage: 'Error: No apps found with name my-app in region us-west-2', expectedTopLevelErrorMessage: - 'No Amplify app found with the specified name in the given region.', + 'No Amplify app found with name "my-app" in region "us-west-2".', errorName: 'AmplifyAppNotFoundError', - expectedDownstreamErrorMessage: - 'Error: No apps found with name my-app in region us-west-2', + expectedDownstreamErrorMessage: undefined, }, ]; diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index 21b50d7132..ba03cbf81f 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -505,7 +505,7 @@ export class CdkErrorMapper { errorRegex: /No apps found with name (?.*) in region (?.*)/, humanReadableErrorMessage: - 'No Amplify app found with the specified name in the given region.', + 'No Amplify app found with name "{appName}" in region "{region}".', resolutionMessage: 'Ensure that an Amplify app named "{appName}" exists in the "{region}" region.', errorName: 'AmplifyAppNotFoundError', From 28a37d62011dc4674effe41fb8c209c9d0e091b9 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Mon, 9 Dec 2024 18:22:50 -0800 Subject: [PATCH 04/14] Updated the code to catch the error app name not available in the region --- ...y-rivers-double.md => ninety-coins-jog.md} | 2 +- .../src/cdk_error_mapper.test.ts | 7 --- .../backend-deployer/src/cdk_error_mapper.ts | 17 +------ .../generate_graphql_client_code_command.ts | 41 +++++++++++----- .../outputs/generate_outputs_command.ts | 48 ++++++++++++------- 5 files changed, 63 insertions(+), 52 deletions(-) rename .changeset/{tasty-rivers-double.md => ninety-coins-jog.md} (64%) diff --git a/.changeset/tasty-rivers-double.md b/.changeset/ninety-coins-jog.md similarity index 64% rename from .changeset/tasty-rivers-double.md rename to .changeset/ninety-coins-jog.md index f86f6d3a8c..462f5f2b66 100644 --- a/.changeset/tasty-rivers-double.md +++ b/.changeset/ninety-coins-jog.md @@ -1,5 +1,5 @@ --- -'@aws-amplify/backend-deployer': patch +'@aws-amplify/backend-cli': patch --- Added error mapping for app name not available in the region diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index 80406f8822..8860260173 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -659,13 +659,6 @@ npm error enoent`, errorName: 'LambdaEmptyZipFault', expectedDownstreamErrorMessage: undefined, }, - { - errorMessage: 'Error: No apps found with name my-app in region us-west-2', - expectedTopLevelErrorMessage: - 'No Amplify app found with name "my-app" in region "us-west-2".', - errorName: 'AmplifyAppNotFoundError', - expectedDownstreamErrorMessage: undefined, - }, ]; void describe('invokeCDKCommand', { concurrency: 1 }, () => { diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index ba03cbf81f..4418fc3275 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -497,20 +497,6 @@ export class CdkErrorMapper { errorName: 'CloudFormationDeploymentError', classification: 'ERROR', }, - // This type of error often occurs when: - // Users have not deployed their app to the specified region. - // There's a mismatch between the app name in the local configuration and the deployed app name. - // Users are working in a different AWS account or region than where the app is deployed. - { - errorRegex: - /No apps found with name (?.*) in region (?.*)/, - humanReadableErrorMessage: - 'No Amplify app found with name "{appName}" in region "{region}".', - resolutionMessage: - 'Ensure that an Amplify app named "{appName}" exists in the "{region}" region.', - errorName: 'AmplifyAppNotFoundError', - classification: 'ERROR', - }, ]; } @@ -542,5 +528,4 @@ export type CDKDeploymentError = | 'SyntaxError' | 'GetLambdaLayerVersionError' | 'LambdaEmptyZipFault' - | 'LambdaMaxSizeExceededError' - | 'AmplifyAppNotFoundError'; + | 'LambdaMaxSizeExceededError'; diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index ec8e4a6ed7..6bb94805c8 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -15,6 +15,7 @@ import { GenerateModelsOptions, } from '@aws-amplify/model-generator'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; type GenerateOptions = | GenerateGraphqlCodegenOptions @@ -89,20 +90,36 @@ export class GenerateGraphqlClientCodeCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args - ); - const out = this.getOutDir(args); - const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; - const formatParams = this.formatParamBuilders[format](args); + try { + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); + const out = this.getOutDir(args); + const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; + const formatParams = this.formatParamBuilders[format](args); + + const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ + ...backendIdentifier, + ...formatParams, + } as unknown as InvokeGenerateApiCodeProps); - const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ - ...backendIdentifier, - ...formatParams, - } as unknown as InvokeGenerateApiCodeProps); + await result.writeToDirectory(out); + } catch (error) { + const appNotFoundMatch = (error as Error).message.match( + /No apps found with name (?.*) in region (?.*)/ + ); - await result.writeToDirectory(out); + if (appNotFoundMatch?.groups) { + const { appName, region } = appNotFoundMatch.groups; + throw new AmplifyUserError('AmplifyAppNotFoundError', { + message: `No Amplify app found with name "${appName}" in region "${region}".`, + resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + }); + } + // Re-throw other errors + throw error; + } }; /** diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 0edb427a00..565292a0b4 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -55,25 +55,41 @@ export class GenerateOutputsCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args + try { + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); + + if (!backendIdentifier) { + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }); + } + + await this.clientConfigGenerator.generateClientConfigToFile( + backendIdentifier, + args.outputsVersion as ClientConfigVersion, + args.outDir, + args.format + ); + } catch (error) { + const appNotFoundMatch = (error as Error).message.match( + /No apps found with name (?.*) in region (?.*)/ ); - if (!backendIdentifier) { - throw new AmplifyUserError('BackendIdentifierResolverError', { - message: 'Could not resolve the backend identifier.', - resolution: - 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }); + if (appNotFoundMatch?.groups) { + const { appName, region } = appNotFoundMatch.groups; + throw new AmplifyUserError('AmplifyAppNotFoundError', { + message: `No Amplify app found with name "${appName}" in region "${region}".`, + resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + }); + } + // Re-throw other errors + throw error; } - - await this.clientConfigGenerator.generateClientConfigToFile( - backendIdentifier, - args.outputsVersion as ClientConfigVersion, - args.outDir, - args.format - ); }; /** From a2c4031d257d241befe136ab9b0cb20905aea45e Mon Sep 17 00:00:00 2001 From: vigy02 Date: Tue, 10 Dec 2024 11:43:06 -0800 Subject: [PATCH 05/14] Updated backend identifier resolver --- .../backend_identifier_resolver.ts | 51 +++++++++++++------ .../generate_graphql_client_code_command.ts | 41 +++++---------- .../outputs/generate_outputs_command.ts | 48 ++++++----------- 3 files changed, 64 insertions(+), 76 deletions(-) diff --git a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts index 0b74437a57..7353a7c3a7 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts @@ -1,7 +1,10 @@ import { DeployedBackendIdentifier } from '@aws-amplify/deployed-backend-client'; import { NamespaceResolver } from './local_namespace_resolver.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; +import { + AmplifyUserError, + BackendIdentifierConversions, +} from '@aws-amplify/platform-core'; export type BackendIdentifierParameters = { stack?: string; @@ -30,21 +33,39 @@ export class AppBackendIdentifierResolver implements BackendIdentifierResolver { resolveDeployedBackendIdentifier = async ( args: BackendIdentifierParameters ): Promise => { - if (args.stack) { - return { stackName: args.stack }; - } else if (args.appId && args.branch) { - return { - namespace: args.appId, - name: args.branch, - type: 'branch', - }; - } else if (args.branch) { - return { - appName: await this.namespaceResolver.resolve(), - branchName: args.branch, - }; + try { + if (args.stack) { + return { stackName: args.stack }; + } else if (args.appId && args.branch) { + return { + namespace: args.appId, + name: args.branch, + type: 'branch', + }; + } else if (args.branch) { + const resolvedNamespace = await this.namespaceResolver.resolve(); + return { + appName: resolvedNamespace, + branchName: args.branch, + }; + } + return undefined; + } catch (error) { + const appNotFoundMatch = (error as Error).message.match( + /No apps found with name (?.*) in region (?.*)/ + ); + + if (appNotFoundMatch?.groups) { + const { appName, region } = appNotFoundMatch.groups; + throw new AmplifyUserError('AmplifyAppNotFoundError', { + message: `No Amplify app found with name "${appName}" in region "${region}".`, + resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + }); + } + + // Re-throw any other errors + throw error; } - return undefined; }; resolveBackendIdentifier = async ( args: BackendIdentifierParameters diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index 6bb94805c8..ec8e4a6ed7 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -15,7 +15,6 @@ import { GenerateModelsOptions, } from '@aws-amplify/model-generator'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; -import { AmplifyUserError } from '@aws-amplify/platform-core'; type GenerateOptions = | GenerateGraphqlCodegenOptions @@ -90,36 +89,20 @@ export class GenerateGraphqlClientCodeCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - try { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args - ); - const out = this.getOutDir(args); - const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; - const formatParams = this.formatParamBuilders[format](args); - - const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ - ...backendIdentifier, - ...formatParams, - } as unknown as InvokeGenerateApiCodeProps); - - await result.writeToDirectory(out); - } catch (error) { - const appNotFoundMatch = (error as Error).message.match( - /No apps found with name (?.*) in region (?.*)/ + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args ); + const out = this.getOutDir(args); + const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; + const formatParams = this.formatParamBuilders[format](args); - if (appNotFoundMatch?.groups) { - const { appName, region } = appNotFoundMatch.groups; - throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: `No Amplify app found with name "${appName}" in region "${region}".`, - resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, - }); - } - // Re-throw other errors - throw error; - } + const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ + ...backendIdentifier, + ...formatParams, + } as unknown as InvokeGenerateApiCodeProps); + + await result.writeToDirectory(out); }; /** diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 565292a0b4..0edb427a00 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -55,41 +55,25 @@ export class GenerateOutputsCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - try { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args - ); - - if (!backendIdentifier) { - throw new AmplifyUserError('BackendIdentifierResolverError', { - message: 'Could not resolve the backend identifier.', - resolution: - 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }); - } - - await this.clientConfigGenerator.generateClientConfigToFile( - backendIdentifier, - args.outputsVersion as ClientConfigVersion, - args.outDir, - args.format - ); - } catch (error) { - const appNotFoundMatch = (error as Error).message.match( - /No apps found with name (?.*) in region (?.*)/ + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args ); - if (appNotFoundMatch?.groups) { - const { appName, region } = appNotFoundMatch.groups; - throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: `No Amplify app found with name "${appName}" in region "${region}".`, - resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, - }); - } - // Re-throw other errors - throw error; + if (!backendIdentifier) { + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }); } + + await this.clientConfigGenerator.generateClientConfigToFile( + backendIdentifier, + args.outputsVersion as ClientConfigVersion, + args.outDir, + args.format + ); }; /** From a5692a06f08d50803d8c6726b7bc9c9fa7626961 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Tue, 10 Dec 2024 14:57:33 -0800 Subject: [PATCH 06/14] Updated to handle the No apps found error --- .../outputs/generate_outputs_command.ts | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 0edb427a00..b11f289cb3 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -55,25 +55,42 @@ export class GenerateOutputsCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args + try { + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); + + if (!backendIdentifier) { + throw new AmplifyUserError('BackendIdentifierResolverError', { + message: 'Could not resolve the backend identifier.', + resolution: + 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', + }); + } + + await this.clientConfigGenerator.generateClientConfigToFile( + backendIdentifier, + args.outputsVersion as ClientConfigVersion, + args.outDir, + args.format + ); + } catch (error) { + const appNotFoundMatch = (error as Error).message.match( + /No apps found with name (?.*) in region (?.*)/ ); - if (!backendIdentifier) { - throw new AmplifyUserError('BackendIdentifierResolverError', { - message: 'Could not resolve the backend identifier.', - resolution: - 'Ensure stack name or Amplify App ID and branch specified are correct and exists, then re-run this command.', - }); - } + if (appNotFoundMatch?.groups) { + const { appName, region } = appNotFoundMatch.groups; + throw new AmplifyUserError('AmplifyAppNotFoundError', { + message: `No Amplify app found with name "${appName}" in region "${region}".`, + resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + }); + } - await this.clientConfigGenerator.generateClientConfigToFile( - backendIdentifier, - args.outputsVersion as ClientConfigVersion, - args.outDir, - args.format - ); + // Re-throw any other errors + throw error; + } }; /** From 6e73f46e8a41cc15ca47ce6bbdea1e602b31e13a Mon Sep 17 00:00:00 2001 From: vigy02 Date: Tue, 10 Dec 2024 15:17:02 -0800 Subject: [PATCH 07/14] Updated generate-graphql command as well to catch the error --- .../backend_identifier_resolver.ts | 52 ++++++------------- .../generate_graphql_client_code_command.ts | 40 ++++++++++---- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts index 7353a7c3a7..877c543835 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts @@ -1,10 +1,7 @@ import { DeployedBackendIdentifier } from '@aws-amplify/deployed-backend-client'; import { NamespaceResolver } from './local_namespace_resolver.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { - AmplifyUserError, - BackendIdentifierConversions, -} from '@aws-amplify/platform-core'; +import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; export type BackendIdentifierParameters = { stack?: string; @@ -33,39 +30,22 @@ export class AppBackendIdentifierResolver implements BackendIdentifierResolver { resolveDeployedBackendIdentifier = async ( args: BackendIdentifierParameters ): Promise => { - try { - if (args.stack) { - return { stackName: args.stack }; - } else if (args.appId && args.branch) { - return { - namespace: args.appId, - name: args.branch, - type: 'branch', - }; - } else if (args.branch) { - const resolvedNamespace = await this.namespaceResolver.resolve(); - return { - appName: resolvedNamespace, - branchName: args.branch, - }; - } - return undefined; - } catch (error) { - const appNotFoundMatch = (error as Error).message.match( - /No apps found with name (?.*) in region (?.*)/ - ); - - if (appNotFoundMatch?.groups) { - const { appName, region } = appNotFoundMatch.groups; - throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: `No Amplify app found with name "${appName}" in region "${region}".`, - resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, - }); - } - - // Re-throw any other errors - throw error; + if (args.stack) { + return { stackName: args.stack }; + } else if (args.appId && args.branch) { + return { + namespace: args.appId, + name: args.branch, + type: 'branch', + }; + } else if (args.branch) { + const resolvedNamespace = await this.namespaceResolver.resolve(); + return { + appName: resolvedNamespace, + branchName: args.branch, + }; } + return undefined; }; resolveBackendIdentifier = async ( args: BackendIdentifierParameters diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index ec8e4a6ed7..71696a651f 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -15,6 +15,7 @@ import { GenerateModelsOptions, } from '@aws-amplify/model-generator'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; type GenerateOptions = | GenerateGraphqlCodegenOptions @@ -89,20 +90,37 @@ export class GenerateGraphqlClientCodeCommand handler = async ( args: ArgumentsCamelCase ): Promise => { - const backendIdentifier = - await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( - args + try { + const backendIdentifier = + await this.backendIdentifierResolver.resolveDeployedBackendIdentifier( + args + ); + const out = this.getOutDir(args); + const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; + const formatParams = this.formatParamBuilders[format](args); + + const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ + ...backendIdentifier, + ...formatParams, + } as unknown as InvokeGenerateApiCodeProps); + + await result.writeToDirectory(out); + } catch (error) { + const appNotFoundMatch = (error as Error).message.match( + /No apps found with name (?.*) in region (?.*)/ ); - const out = this.getOutDir(args); - const format = args.format ?? GenerateApiCodeFormat.GRAPHQL_CODEGEN; - const formatParams = this.formatParamBuilders[format](args); - const result = await this.generateApiCodeAdapter.invokeGenerateApiCode({ - ...backendIdentifier, - ...formatParams, - } as unknown as InvokeGenerateApiCodeProps); + if (appNotFoundMatch?.groups) { + const { appName, region } = appNotFoundMatch.groups; + throw new AmplifyUserError('AmplifyAppNotFoundError', { + message: `No Amplify app found with name "${appName}" in region "${region}".`, + resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + }); + } - await result.writeToDirectory(out); + // Re-throw any other errors + throw error; + } }; /** From 33b2ccd3b0ad2f7a61a115257062ed9fa2a8dcc2 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Tue, 10 Dec 2024 15:19:27 -0800 Subject: [PATCH 08/14] reset the backend resolver --- .../cli/src/backend-identifier/backend_identifier_resolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts index 877c543835..0b74437a57 100644 --- a/packages/cli/src/backend-identifier/backend_identifier_resolver.ts +++ b/packages/cli/src/backend-identifier/backend_identifier_resolver.ts @@ -39,9 +39,8 @@ export class AppBackendIdentifierResolver implements BackendIdentifierResolver { type: 'branch', }; } else if (args.branch) { - const resolvedNamespace = await this.namespaceResolver.resolve(); return { - appName: resolvedNamespace, + appName: await this.namespaceResolver.resolve(), branchName: args.branch, }; } From 536dad0e9000fa03b7443ff9d30586bf7b4cee58 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 10:22:12 -0800 Subject: [PATCH 09/14] Updated the error code and added test cases to test the error handling --- ...nerate_graphql_client_code_command.test.ts | 151 ++++++++++++++++++ .../generate_graphql_client_code_command.ts | 19 +-- .../outputs/generate_outputs_command.test.ts | 103 ++++++++++++ .../outputs/generate_outputs_command.ts | 19 +-- .../src/backend_output_client_factory.ts | 1 + ...ame_and_branch_main_stack_name_resolver.ts | 7 +- 6 files changed, 281 insertions(+), 19 deletions(-) diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts index 004d94a267..eeb002d292 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts @@ -18,6 +18,11 @@ import { BackendIdentifierResolverWithFallback } from '../../../backend-identifi import { S3Client } from '@aws-sdk/client-s3'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; void describe('generate graphql-client-code command', () => { const generateApiCodeAdapter = new GenerateApiCodeAdapter({ @@ -356,3 +361,149 @@ void describe('generate graphql-client-code command', () => { assert.match(output, /Arguments .* are mutually exclusive/); }); }); + +void describe('GenerateGraphqlClientCodeCommand error handling', () => { + let generateApiCodeAdapter: GenerateApiCodeAdapter; + let backendIdentifierResolver: AppBackendIdentifierResolver; + let generateGraphqlClientCodeCommand: GenerateGraphqlClientCodeCommand; + + beforeEach(() => { + // Mock the dependencies + generateApiCodeAdapter = { + invokeGenerateApiCode: mock.fn(), + } as unknown as GenerateApiCodeAdapter; + + backendIdentifierResolver = { + resolveDeployedBackendIdentifier: mock.fn(), + } as unknown as AppBackendIdentifierResolver; + + generateGraphqlClientCodeCommand = new GenerateGraphqlClientCodeCommand( + generateApiCodeAdapter, + backendIdentifierResolver + ); + }); + + void it('should throw AmplifyUserError when NO_APP_FOUND_ERROR occurs', async () => { + // Mock the resolver to simulate successful resolution + mock.method( + backendIdentifierResolver, + 'resolveDeployedBackendIdentifier', + () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) + ); + + // Mock the adapter to throw NO_APP_FOUND_ERROR + mock.method(generateApiCodeAdapter, 'invokeGenerateApiCode', () => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_APP_FOUND_ERROR, + 'No Amplify app found in the specified region' + ); + }); + + try { + await generateGraphqlClientCodeCommand.handler({ + stack: undefined, + appId: 'test-app', + 'app-id': 'test-app', + branch: 'main', + format: undefined, + modelTarget: undefined, + 'model-target': undefined, + statementTarget: undefined, + 'statement-target': undefined, + typeTarget: undefined, + 'type-target': undefined, + out: undefined, + modelGenerateIndexRules: undefined, + 'model-generate-index-rules': undefined, + modelEmitAuthProvider: undefined, + 'model-emit-auth-provider': undefined, + modelRespectPrimaryKeyAttributesOnConnectionField: undefined, + 'model-respect-primary-key-attributes-on-connection-field': undefined, + modelGenerateModelsForLazyLoadAndCustomSelectionSet: undefined, + 'model-generate-models-for-lazy-load-and-custom-selection-set': + undefined, + modelAddTimestampFields: undefined, + 'model-add-timestamp-fields': undefined, + modelHandleListNullabilityTransparently: undefined, + 'model-handle-list-nullability-transparently': undefined, + statementMaxDepth: undefined, + 'statement-max-depth': undefined, + statementTypenameIntrospection: undefined, + 'statement-typename-introspection': undefined, + typeMultipleSwiftFiles: undefined, + 'type-multiple-swift-files': undefined, + _: [], + $0: 'command-name', + }); + assert.fail('Expected error was not thrown'); + } catch (error) { + if (error instanceof AmplifyUserError) { + assert.equal(error.name, 'AmplifyAppNotFoundError'); + assert.equal( + error.message, + 'No Amplify app found in the specified region' + ); + assert.equal( + error.resolution, + 'Ensure that an Amplify app exists in the region.' + ); + } + } + }); + + void it('should re-throw other types of errors', async () => { + // Mock the resolver to simulate successful resolution + mock.method( + backendIdentifierResolver, + 'resolveDeployedBackendIdentifier', + () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) + ); + + // Mock the adapter to throw a different type of error + const originalError = new Error('Some other error'); + mock.method(generateApiCodeAdapter, 'invokeGenerateApiCode', () => { + throw originalError; + }); + + try { + await generateGraphqlClientCodeCommand.handler({ + stack: undefined, + appId: 'test-app', + 'app-id': 'test-app', + branch: 'main', + format: undefined, + modelTarget: undefined, + 'model-target': undefined, + statementTarget: undefined, + 'statement-target': undefined, + typeTarget: undefined, + 'type-target': undefined, + out: undefined, + modelGenerateIndexRules: undefined, + 'model-generate-index-rules': undefined, + modelEmitAuthProvider: undefined, + 'model-emit-auth-provider': undefined, + modelRespectPrimaryKeyAttributesOnConnectionField: undefined, + 'model-respect-primary-key-attributes-on-connection-field': undefined, + modelGenerateModelsForLazyLoadAndCustomSelectionSet: undefined, + 'model-generate-models-for-lazy-load-and-custom-selection-set': + undefined, + modelAddTimestampFields: undefined, + 'model-add-timestamp-fields': undefined, + modelHandleListNullabilityTransparently: undefined, + 'model-handle-list-nullability-transparently': undefined, + statementMaxDepth: undefined, + 'statement-max-depth': undefined, + statementTypenameIntrospection: undefined, + 'statement-typename-introspection': undefined, + typeMultipleSwiftFiles: undefined, + 'type-multiple-swift-files': undefined, + _: [], + $0: 'command-name', + }); + assert.fail('Expected error was not thrown'); + } catch (error) { + assert.equal(error, originalError); + } + }); +}); diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index 71696a651f..a4cebec5c5 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -16,6 +16,10 @@ import { } from '@aws-amplify/model-generator'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; type GenerateOptions = | GenerateGraphqlCodegenOptions @@ -106,18 +110,15 @@ export class GenerateGraphqlClientCodeCommand await result.writeToDirectory(out); } catch (error) { - const appNotFoundMatch = (error as Error).message.match( - /No apps found with name (?.*) in region (?.*)/ - ); - - if (appNotFoundMatch?.groups) { - const { appName, region } = appNotFoundMatch.groups; + if ( + error instanceof BackendOutputClientError && + error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR + ) { throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: `No Amplify app found with name "${appName}" in region "${region}".`, - resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + message: error.message, + resolution: `Ensure that an Amplify app exists in the region.`, }); } - // Re-throw any other errors throw error; } diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index 84b4a0f8e6..ad1169eb3f 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts @@ -11,6 +11,11 @@ import { SandboxBackendIdResolver } from '../../sandbox/sandbox_id_resolver.js'; import { S3Client } from '@aws-sdk/client-s3'; import { AmplifyClient } from '@aws-sdk/client-amplify'; import { CloudFormationClient } from '@aws-sdk/client-cloudformation'; +import { + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; void describe('generate outputs command', () => { const clientConfigGeneratorAdapter = new ClientConfigGeneratorAdapter({ @@ -249,3 +254,101 @@ void describe('generate outputs command', () => { assert.match(output, /Arguments .* mutually exclusive/); }); }); + +void describe('GenerateOutputsCommand error handling', () => { + let clientConfigGenerator: ClientConfigGeneratorAdapter; + let backendIdentifierResolver: AppBackendIdentifierResolver; + let generateOutputsCommand: GenerateOutputsCommand; + + beforeEach(() => { + // Mock the dependencies + clientConfigGenerator = { + generateClientConfigToFile: mock.fn(), + } as unknown as ClientConfigGeneratorAdapter; + + backendIdentifierResolver = { + resolveDeployedBackendIdentifier: mock.fn(), + } as unknown as AppBackendIdentifierResolver; + + generateOutputsCommand = new GenerateOutputsCommand( + clientConfigGenerator, + backendIdentifierResolver + ); + }); + + void it('should throw AmplifyUserError when NO_APP_FOUND_ERROR occurs', async () => { + // Mock the resolver to simulate successful resolution + mock.method( + backendIdentifierResolver, + 'resolveDeployedBackendIdentifier', + () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) + ); + + // Mock the generator to throw NO_APP_FOUND_ERROR + mock.method(clientConfigGenerator, 'generateClientConfigToFile', () => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_APP_FOUND_ERROR, + 'No Amplify app found in the specified region' + ); + }); + try { + await generateOutputsCommand.handler({ + stack: undefined, + appId: 'test-app', + 'app-id': 'test-app', + branch: 'main', + format: undefined, + outDir: undefined, + 'out-dir': undefined, + outputsVersion: '1.3', + 'outputs-version': '1.3', + _: [], + $0: 'command-name', + }); + assert.fail('Expected error was not thrown'); + } catch (error) { + if (error instanceof AmplifyUserError) { + assert.equal(error.name, 'AmplifyAppNotFoundError'); + assert.equal( + error.message, + 'No Amplify app found in the specified region' + ); + } + } + }); + + void it('should re-throw other types of errors', async () => { + // Mock the resolver to simulate successful resolution + mock.method( + backendIdentifierResolver, + 'resolveDeployedBackendIdentifier', + () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) + ); + + // Mock the generator to throw a different type of error + const originalError = new Error('Some other error'); + mock.method(clientConfigGenerator, 'generateClientConfigToFile', () => { + throw originalError; + }); + + try { + await generateOutputsCommand.handler({ + stack: undefined, + appId: 'test-app', + 'app-id': 'test-app', + branch: 'main', + format: undefined, + outDir: undefined, + 'out-dir': undefined, + outputsVersion: '1.3', + 'outputs-version': '1.3', + _: [], + $0: 'command-name', + }); + assert.fail('Expected error was not thrown'); + } catch (error) { + assert.equal(error, originalError); + assert(!(error instanceof AmplifyUserError)); + } + }); +}); diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index b11f289cb3..4178869c54 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -9,6 +9,10 @@ import { BackendIdentifierResolver } from '../../../backend-identifier/backend_i import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js'; import { ArgumentsKebabCase } from '../../../kebab_case.js'; import { AmplifyUserError } from '@aws-amplify/platform-core'; +import { + BackendOutputClientError, + BackendOutputClientErrorType, +} from '@aws-amplify/deployed-backend-client'; export type GenerateOutputsCommandOptions = ArgumentsKebabCase; @@ -76,18 +80,15 @@ export class GenerateOutputsCommand args.format ); } catch (error) { - const appNotFoundMatch = (error as Error).message.match( - /No apps found with name (?.*) in region (?.*)/ - ); - - if (appNotFoundMatch?.groups) { - const { appName, region } = appNotFoundMatch.groups; + if ( + error instanceof BackendOutputClientError && + error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR + ) { throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: `No Amplify app found with name "${appName}" in region "${region}".`, - resolution: `Ensure that an Amplify app named "${appName}" exists in the "${region}" region.`, + message: error.message, + resolution: `Ensure that an Amplify app exists in the region.`, }); } - // Re-throw any other errors throw error; } diff --git a/packages/deployed-backend-client/src/backend_output_client_factory.ts b/packages/deployed-backend-client/src/backend_output_client_factory.ts index 2dca755d66..a6cc4c3b46 100644 --- a/packages/deployed-backend-client/src/backend_output_client_factory.ts +++ b/packages/deployed-backend-client/src/backend_output_client_factory.ts @@ -13,6 +13,7 @@ export enum BackendOutputClientErrorType { NO_STACK_FOUND = 'NoStackFound', CREDENTIALS_ERROR = 'CredentialsError', ACCESS_DENIED = 'AccessDenied', + NO_APP_FOUND_ERROR = 'NoAppFoundError', } /** * Error type for BackendOutputClientError diff --git a/packages/deployed-backend-client/src/stack-name-resolvers/app_name_and_branch_main_stack_name_resolver.ts b/packages/deployed-backend-client/src/stack-name-resolvers/app_name_and_branch_main_stack_name_resolver.ts index 5e5ee7318d..41672b28c2 100644 --- a/packages/deployed-backend-client/src/stack-name-resolvers/app_name_and_branch_main_stack_name_resolver.ts +++ b/packages/deployed-backend-client/src/stack-name-resolvers/app_name_and_branch_main_stack_name_resolver.ts @@ -1,6 +1,10 @@ import { MainStackNameResolver } from '@aws-amplify/plugin-types'; import { AmplifyClient, ListAppsCommand } from '@aws-sdk/client-amplify'; import { BackendIdentifierConversions } from '@aws-amplify/platform-core'; +import { + BackendOutputClientError, + BackendOutputClientErrorType, +} from '../backend_output_client_factory.js'; /** * Tuple of Amplify App name and branch @@ -38,7 +42,8 @@ export class AppNameAndBranchMainStackNameResolver ); const region = await this.amplifyClient.config.region(); if (appMatches.length === 0) { - throw new Error( + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_APP_FOUND_ERROR, `No apps found with name ${this.appNameAndBranch.appName} in region ${region}` ); } else if (appMatches.length >= 2) { From eab450f8abbda96e5dfcf6310f8e41a8be233388 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 10:26:29 -0800 Subject: [PATCH 10/14] chore: Updated the API --- packages/client-config/API.md | 2 +- packages/deployed-backend-client/API.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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/deployed-backend-client/API.md b/packages/deployed-backend-client/API.md index c0dfc59472..5864811b91 100644 --- a/packages/deployed-backend-client/API.md +++ b/packages/deployed-backend-client/API.md @@ -102,6 +102,8 @@ export enum BackendOutputClientErrorType { // (undocumented) METADATA_RETRIEVAL_ERROR = "MetadataRetrievalError", // (undocumented) + NO_APP_FOUND_ERROR = "NoAppFoundError", + // (undocumented) NO_OUTPUTS_FOUND = "NoOutputsFound", // (undocumented) NO_STACK_FOUND = "NoStackFound" From b400709a803727bf05a2e9405ee2085cbaeb8092 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 10:30:14 -0800 Subject: [PATCH 11/14] chore: updated API --- 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 37b62bb00f8785944e4e6693506a7ab680989e12 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 10:42:54 -0800 Subject: [PATCH 12/14] Updated error handling to show the full error details --- .../generate_graphql_client_code_command.ts | 12 ++++++++---- .../generate/outputs/generate_outputs_command.ts | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts index a4cebec5c5..d4b7268a4c 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.ts @@ -114,10 +114,14 @@ export class GenerateGraphqlClientCodeCommand error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR ) { - throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: error.message, - resolution: `Ensure that an Amplify app exists in the region.`, - }); + throw new AmplifyUserError( + 'AmplifyAppNotFoundError', + { + message: error.message, + resolution: `Ensure that an Amplify app exists in the region.`, + }, + error + ); } // Re-throw any other errors throw error; diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts index 4178869c54..0c9b258866 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.ts @@ -84,10 +84,14 @@ export class GenerateOutputsCommand error instanceof BackendOutputClientError && error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR ) { - throw new AmplifyUserError('AmplifyAppNotFoundError', { - message: error.message, - resolution: `Ensure that an Amplify app exists in the region.`, - }); + throw new AmplifyUserError( + 'AmplifyAppNotFoundError', + { + message: error.message, + resolution: `Ensure that an Amplify app exists in the region.`, + }, + error + ); } // Re-throw any other errors throw error; From 3236c1e9d316ddfb74c1c222773a3cd758794260 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 15:16:23 -0800 Subject: [PATCH 13/14] added the unit test cases to the existing test suite --- ...nerate_graphql_client_code_command.test.ts | 161 ++++-------------- .../outputs/generate_outputs_command.test.ts | 132 +++++--------- 2 files changed, 70 insertions(+), 223 deletions(-) diff --git a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts index eeb002d292..e09c76036f 100644 --- a/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts +++ b/packages/cli/src/commands/generate/graphql-client-code/generate_graphql_client_code_command.test.ts @@ -1,7 +1,10 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { GenerateGraphqlClientCodeCommand } from './generate_graphql_client_code_command.js'; import yargs, { CommandModule } from 'yargs'; -import { TestCommandRunner } from '../../../test-utils/command_runner.js'; +import { + TestCommandError, + TestCommandRunner, +} from '../../../test-utils/command_runner.js'; import assert from 'node:assert'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; import path from 'path'; @@ -22,7 +25,6 @@ import { BackendOutputClientError, BackendOutputClientErrorType, } from '@aws-amplify/deployed-backend-client'; -import { AmplifyUserError } from '@aws-amplify/platform-core'; void describe('generate graphql-client-code command', () => { const generateApiCodeAdapter = new GenerateApiCodeAdapter({ @@ -360,150 +362,45 @@ void describe('generate graphql-client-code command', () => { ); assert.match(output, /Arguments .* are mutually exclusive/); }); -}); - -void describe('GenerateGraphqlClientCodeCommand error handling', () => { - let generateApiCodeAdapter: GenerateApiCodeAdapter; - let backendIdentifierResolver: AppBackendIdentifierResolver; - let generateGraphqlClientCodeCommand: GenerateGraphqlClientCodeCommand; - - beforeEach(() => { - // Mock the dependencies - generateApiCodeAdapter = { - invokeGenerateApiCode: mock.fn(), - } as unknown as GenerateApiCodeAdapter; - - backendIdentifierResolver = { - resolveDeployedBackendIdentifier: mock.fn(), - } as unknown as AppBackendIdentifierResolver; - - generateGraphqlClientCodeCommand = new GenerateGraphqlClientCodeCommand( - generateApiCodeAdapter, - backendIdentifierResolver - ); - }); - void it('should throw AmplifyUserError when NO_APP_FOUND_ERROR occurs', async () => { - // Mock the resolver to simulate successful resolution - mock.method( - backendIdentifierResolver, - 'resolveDeployedBackendIdentifier', - () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) - ); - - // Mock the adapter to throw NO_APP_FOUND_ERROR + void it('throws user error when NO_APP_FOUND_ERROR occurs', async () => { mock.method(generateApiCodeAdapter, 'invokeGenerateApiCode', () => { throw new BackendOutputClientError( BackendOutputClientErrorType.NO_APP_FOUND_ERROR, - 'No Amplify app found in the specified region' + 'No app found for stack stack_name' ); }); - try { - await generateGraphqlClientCodeCommand.handler({ - stack: undefined, - appId: 'test-app', - 'app-id': 'test-app', - branch: 'main', - format: undefined, - modelTarget: undefined, - 'model-target': undefined, - statementTarget: undefined, - 'statement-target': undefined, - typeTarget: undefined, - 'type-target': undefined, - out: undefined, - modelGenerateIndexRules: undefined, - 'model-generate-index-rules': undefined, - modelEmitAuthProvider: undefined, - 'model-emit-auth-provider': undefined, - modelRespectPrimaryKeyAttributesOnConnectionField: undefined, - 'model-respect-primary-key-attributes-on-connection-field': undefined, - modelGenerateModelsForLazyLoadAndCustomSelectionSet: undefined, - 'model-generate-models-for-lazy-load-and-custom-selection-set': - undefined, - modelAddTimestampFields: undefined, - 'model-add-timestamp-fields': undefined, - modelHandleListNullabilityTransparently: undefined, - 'model-handle-list-nullability-transparently': undefined, - statementMaxDepth: undefined, - 'statement-max-depth': undefined, - statementTypenameIntrospection: undefined, - 'statement-typename-introspection': undefined, - typeMultipleSwiftFiles: undefined, - 'type-multiple-swift-files': undefined, - _: [], - $0: 'command-name', - }); - assert.fail('Expected error was not thrown'); - } catch (error) { - if (error instanceof AmplifyUserError) { - assert.equal(error.name, 'AmplifyAppNotFoundError'); - assert.equal( - error.message, - 'No Amplify app found in the specified region' - ); - assert.equal( - error.resolution, - 'Ensure that an Amplify app exists in the region.' + await assert.rejects( + () => + commandRunner.runCommand( + 'graphql-client-code --app-id test-app --branch main' + ), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'AmplifyAppNotFoundError'); + assert.strictEqual( + error.error.message, + 'No app found for stack stack_name' ); + return true; } - } - }); - - void it('should re-throw other types of errors', async () => { - // Mock the resolver to simulate successful resolution - mock.method( - backendIdentifierResolver, - 'resolveDeployedBackendIdentifier', - () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) ); + }); - // Mock the adapter to throw a different type of error + void it('re-throw other types of errors', async () => { const originalError = new Error('Some other error'); mock.method(generateApiCodeAdapter, 'invokeGenerateApiCode', () => { throw originalError; }); - - try { - await generateGraphqlClientCodeCommand.handler({ - stack: undefined, - appId: 'test-app', - 'app-id': 'test-app', - branch: 'main', - format: undefined, - modelTarget: undefined, - 'model-target': undefined, - statementTarget: undefined, - 'statement-target': undefined, - typeTarget: undefined, - 'type-target': undefined, - out: undefined, - modelGenerateIndexRules: undefined, - 'model-generate-index-rules': undefined, - modelEmitAuthProvider: undefined, - 'model-emit-auth-provider': undefined, - modelRespectPrimaryKeyAttributesOnConnectionField: undefined, - 'model-respect-primary-key-attributes-on-connection-field': undefined, - modelGenerateModelsForLazyLoadAndCustomSelectionSet: undefined, - 'model-generate-models-for-lazy-load-and-custom-selection-set': - undefined, - modelAddTimestampFields: undefined, - 'model-add-timestamp-fields': undefined, - modelHandleListNullabilityTransparently: undefined, - 'model-handle-list-nullability-transparently': undefined, - statementMaxDepth: undefined, - 'statement-max-depth': undefined, - statementTypenameIntrospection: undefined, - 'statement-typename-introspection': undefined, - typeMultipleSwiftFiles: undefined, - 'type-multiple-swift-files': undefined, - _: [], - $0: 'command-name', - }); - assert.fail('Expected error was not thrown'); - } catch (error) { - assert.equal(error, originalError); - } + await assert.rejects( + () => + commandRunner.runCommand( + 'graphql-client-code --app-id test-app --branch main' + ), + (error: TestCommandError) => { + assert.strictEqual(error.error, originalError); + return true; + } + ); }); }); diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index ad1169eb3f..aaa80c6b9a 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts @@ -2,7 +2,10 @@ import { beforeEach, describe, it, mock } from 'node:test'; import { GenerateOutputsCommand } from './generate_outputs_command.js'; import { ClientConfigFormat } from '@aws-amplify/client-config'; import yargs, { CommandModule } from 'yargs'; -import { TestCommandRunner } from '../../../test-utils/command_runner.js'; +import { + TestCommandError, + TestCommandRunner, +} from '../../../test-utils/command_runner.js'; import assert from 'node:assert'; import { AppBackendIdentifierResolver } from '../../../backend-identifier/backend_identifier_resolver.js'; import { ClientConfigGeneratorAdapter } from '../../../client-config/client_config_generator_adapter.js'; @@ -15,7 +18,6 @@ import { BackendOutputClientError, BackendOutputClientErrorType, } from '@aws-amplify/deployed-backend-client'; -import { AmplifyUserError } from '@aws-amplify/platform-core'; void describe('generate outputs command', () => { const clientConfigGeneratorAdapter = new ClientConfigGeneratorAdapter({ @@ -253,102 +255,50 @@ void describe('generate outputs command', () => { ); assert.match(output, /Arguments .* mutually exclusive/); }); -}); -void describe('GenerateOutputsCommand error handling', () => { - let clientConfigGenerator: ClientConfigGeneratorAdapter; - let backendIdentifierResolver: AppBackendIdentifierResolver; - let generateOutputsCommand: GenerateOutputsCommand; - - beforeEach(() => { - // Mock the dependencies - clientConfigGenerator = { - generateClientConfigToFile: mock.fn(), - } as unknown as ClientConfigGeneratorAdapter; - - backendIdentifierResolver = { - resolveDeployedBackendIdentifier: mock.fn(), - } as unknown as AppBackendIdentifierResolver; - - generateOutputsCommand = new GenerateOutputsCommand( - clientConfigGenerator, - backendIdentifierResolver - ); - }); - - void it('should throw AmplifyUserError when NO_APP_FOUND_ERROR occurs', async () => { - // Mock the resolver to simulate successful resolution + void it('throws user error when NO_APP_FOUND_ERROR occurs', async () => { + // Mock the generator to throw NO_APP_FOUND_ERROR mock.method( - backendIdentifierResolver, - 'resolveDeployedBackendIdentifier', - () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) + clientConfigGeneratorAdapter, + 'generateClientConfigToFile', + () => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_APP_FOUND_ERROR, + 'No Amplify app found in the specified region' + ); + } ); - // Mock the generator to throw NO_APP_FOUND_ERROR - mock.method(clientConfigGenerator, 'generateClientConfigToFile', () => { - throw new BackendOutputClientError( - BackendOutputClientErrorType.NO_APP_FOUND_ERROR, - 'No Amplify app found in the specified region' - ); - }); - try { - await generateOutputsCommand.handler({ - stack: undefined, - appId: 'test-app', - 'app-id': 'test-app', - branch: 'main', - format: undefined, - outDir: undefined, - 'out-dir': undefined, - outputsVersion: '1.3', - 'outputs-version': '1.3', - _: [], - $0: 'command-name', - }); - assert.fail('Expected error was not thrown'); - } catch (error) { - if (error instanceof AmplifyUserError) { - assert.equal(error.name, 'AmplifyAppNotFoundError'); - assert.equal( - error.message, + await assert.rejects( + () => commandRunner.runCommand('outputs --app-id test-app --branch main'), + (error: TestCommandError) => { + assert.strictEqual(error.error.name, 'AmplifyAppNotFoundError'); + assert.strictEqual( + error.error.message, 'No Amplify app found in the specified region' ); + return true; } - } - }); - - void it('should re-throw other types of errors', async () => { - // Mock the resolver to simulate successful resolution - mock.method( - backendIdentifierResolver, - 'resolveDeployedBackendIdentifier', - () => Promise.resolve({ appId: 'test-app', branchName: 'main' }) ); - - // Mock the generator to throw a different type of error - const originalError = new Error('Some other error'); - mock.method(clientConfigGenerator, 'generateClientConfigToFile', () => { - throw originalError; - }); - - try { - await generateOutputsCommand.handler({ - stack: undefined, - appId: 'test-app', - 'app-id': 'test-app', - branch: 'main', - format: undefined, - outDir: undefined, - 'out-dir': undefined, - outputsVersion: '1.3', - 'outputs-version': '1.3', - _: [], - $0: 'command-name', - }); - assert.fail('Expected error was not thrown'); - } catch (error) { - assert.equal(error, originalError); - assert(!(error instanceof AmplifyUserError)); - } }); + void it('re-throw other types of errors'), + async () => { + const originalError = new Error('Some other error'); + mock.method( + clientConfigGeneratorAdapter, + 'generateClientConfigToFile', + () => { + throw originalError; + } + ); + + await assert.rejects( + () => + commandRunner.runCommand('outputs --app-id test-app --branch main'), + (error: Error) => { + assert.strictEqual(error, originalError); + return true; + } + ); + }; }); From 31b406a02966f853b24886e0cb4b0ffe01d62b92 Mon Sep 17 00:00:00 2001 From: vigy02 Date: Fri, 13 Dec 2024 15:48:08 -0800 Subject: [PATCH 14/14] fixed lint errors --- .../outputs/generate_outputs_command.test.ts | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index aaa80c6b9a..462f077312 100644 --- a/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts +++ b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts @@ -281,24 +281,21 @@ void describe('generate outputs command', () => { } ); }); - void it('re-throw other types of errors'), - async () => { - const originalError = new Error('Some other error'); - mock.method( - clientConfigGeneratorAdapter, - 'generateClientConfigToFile', - () => { - throw originalError; - } - ); - - await assert.rejects( - () => - commandRunner.runCommand('outputs --app-id test-app --branch main'), - (error: Error) => { - assert.strictEqual(error, originalError); - return true; - } - ); - }; + void it('re-throw other types of errors', async () => { + const originalError = new Error('Some other error'); + mock.method( + clientConfigGeneratorAdapter, + 'generateClientConfigToFile', + () => { + throw originalError; + } + ); + await assert.rejects( + () => commandRunner.runCommand('outputs --app-id test-app --branch main'), + (error: TestCommandError) => { + assert.strictEqual(error.error, originalError); + return true; + } + ); + }); });