diff --git a/.changeset/ninety-coins-jog.md b/.changeset/ninety-coins-jog.md new file mode 100644 index 00000000000..af73fe643e1 --- /dev/null +++ b/.changeset/ninety-coins-jog.md @@ -0,0 +1,7 @@ +--- +'@aws-amplify/deployed-backend-client': patch +'@aws-amplify/client-config': patch +'@aws-amplify/backend-cli': patch +--- + +Add error mapping for app name not available in the region 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 004d94a2670..e09c76036fc 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'; @@ -18,6 +21,10 @@ 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'; void describe('generate graphql-client-code command', () => { const generateApiCodeAdapter = new GenerateApiCodeAdapter({ @@ -355,4 +362,45 @@ void describe('generate graphql-client-code command', () => { ); assert.match(output, /Arguments .* are mutually exclusive/); }); + + 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 app found for stack stack_name' + ); + }); + + 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('re-throw other types of errors', async () => { + const originalError = new Error('Some other error'); + mock.method(generateApiCodeAdapter, 'invokeGenerateApiCode', () => { + throw 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/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 ec8e4a6ed7e..40620b9eded 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,11 @@ import { GenerateModelsOptions, } 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 @@ -89,20 +94,38 @@ 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); + await result.writeToDirectory(out); + } catch (error) { + if ( + BackendOutputClientError.isBackendOutputClientError(error) && + error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR + ) { + 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.test.ts b/packages/cli/src/commands/generate/outputs/generate_outputs_command.test.ts index 84b4a0f8e6c..462f0773128 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'; @@ -11,6 +14,10 @@ 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'; void describe('generate outputs command', () => { const clientConfigGeneratorAdapter = new ClientConfigGeneratorAdapter({ @@ -248,4 +255,47 @@ void describe('generate outputs command', () => { ); assert.match(output, /Arguments .* mutually exclusive/); }); + + void it('throws user error when NO_APP_FOUND_ERROR occurs', async () => { + // Mock the generator to throw NO_APP_FOUND_ERROR + mock.method( + clientConfigGeneratorAdapter, + 'generateClientConfigToFile', + () => { + throw new BackendOutputClientError( + BackendOutputClientErrorType.NO_APP_FOUND_ERROR, + 'No Amplify app found in the specified region' + ); + } + ); + + 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('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; + } + ); + }); }); 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 0edb427a00e..e71afddf289 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; @@ -55,25 +59,43 @@ 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.', - }); - } + 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 - ); + await this.clientConfigGenerator.generateClientConfigToFile( + backendIdentifier, + args.outputsVersion as ClientConfigVersion, + args.outDir, + args.format + ); + } catch (error) { + if ( + BackendOutputClientError.isBackendOutputClientError(error) && + error.code === BackendOutputClientErrorType.NO_APP_FOUND_ERROR + ) { + 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/client-config/src/unified_client_config_generator.ts b/packages/client-config/src/unified_client_config_generator.ts index 068b2c03493..cf771a7dfe3 100644 --- a/packages/client-config/src/unified_client_config_generator.ts +++ b/packages/client-config/src/unified_client_config_generator.ts @@ -103,6 +103,15 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator { }, error ); + case BackendOutputClientErrorType.NO_APP_FOUND_ERROR: + throw new AmplifyUserError( + 'AmplifyAppNotFoundError', + { + message: error.message, + resolution: `Ensure that an Amplify app exists in the region.`, + }, + error + ); } } throw error; diff --git a/packages/deployed-backend-client/API.md b/packages/deployed-backend-client/API.md index 3c5353103ac..af522a516c5 100644 --- a/packages/deployed-backend-client/API.md +++ b/packages/deployed-backend-client/API.md @@ -103,6 +103,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" 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 f9e553f369d..6220d1e472c 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