diff --git a/.changeset/curly-toes-doubt.md b/.changeset/curly-toes-doubt.md new file mode 100644 index 0000000000..5bc9d55c88 --- /dev/null +++ b/.changeset/curly-toes-doubt.md @@ -0,0 +1,11 @@ +--- +'@aws-amplify/integration-tests': patch +'@aws-amplify/backend-deployer': patch +'create-amplify': patch +'@aws-amplify/platform-core': patch +'@aws-amplify/backend-data': patch +'@aws-amplify/sandbox': patch +'@aws-amplify/backend-cli': patch +--- + +Refactor error handling, introduce two new AmplifyErrors diff --git a/packages/backend-data/src/factory.ts b/packages/backend-data/src/factory.ts index e42ef59656..f947a2e11e 100644 --- a/packages/backend-data/src/factory.ts +++ b/packages/backend-data/src/factory.ts @@ -24,6 +24,7 @@ import { isUsingDefaultApiKeyAuth, } from './convert_authorization_modes.js'; import { validateAuthorizationModes } from './validate_authorization_modes.js'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; /** * Singleton factory for AmplifyGraphqlApi constructs that can be used in Amplify project files @@ -80,16 +81,44 @@ class DataGenerator implements ConstructContainerEntryGenerator { ) {} generateContainerEntry = (scope: Construct) => { - const authorizationModes = convertAuthorizationModesToCDK( - this.functionInstanceProvider, - this.providedAuthConfig, - this.props.authorizationModes - ); + let authorizationModes; - validateAuthorizationModes( - this.props.authorizationModes, - authorizationModes - ); + try { + authorizationModes = convertAuthorizationModesToCDK( + this.functionInstanceProvider, + this.providedAuthConfig, + this.props.authorizationModes + ); + } catch (error) { + throw new AmplifyUserError( + 'InvalidSchemaAuthError', + { + message: + error instanceof Error + ? error.message + : 'Cannot covert authorization modes', + }, + error instanceof Error ? error : undefined + ); + } + + try { + validateAuthorizationModes( + this.props.authorizationModes, + authorizationModes + ); + } catch (error) { + throw new AmplifyUserError( + 'InvalidSchemaAuthError', + { + message: + error instanceof Error + ? error.message + : 'Failed to validate authorization modes', + }, + error instanceof Error ? error : undefined + ); + } const sandboxModeEnabled = isUsingDefaultApiKeyAuth( this.providedAuthConfig, @@ -101,9 +130,25 @@ class DataGenerator implements ConstructContainerEntryGenerator { this.props.functions ?? {} ); + let amplifyGraphqlDefinition; + try { + amplifyGraphqlDefinition = convertSchemaToCDK(this.props.schema); + } catch (error) { + throw new AmplifyUserError( + 'InvalidSchemaError', + { + message: + error instanceof Error + ? error.message + : 'Cannot covert user schema', + }, + error instanceof Error ? error : undefined + ); + } + return new AmplifyData(scope, this.defaultName, { apiName: this.props.name, - definition: convertSchemaToCDK(this.props.schema), + definition: amplifyGraphqlDefinition, authorizationModes, outputStorageStrategy: this.outputStorageStrategy, functionNameMap, diff --git a/packages/backend-deployer/src/cdk_deployer.test.ts b/packages/backend-deployer/src/cdk_deployer.test.ts index d356da969e..47c45e1c37 100644 --- a/packages/backend-deployer/src/cdk_deployer.test.ts +++ b/packages/backend-deployer/src/cdk_deployer.test.ts @@ -1,7 +1,7 @@ import { after, beforeEach, describe, it, mock } from 'node:test'; import { CDKDeployer } from './cdk_deployer.js'; import assert from 'node:assert'; -import { BackendLocator } from '@aws-amplify/platform-core'; +import { AmplifyError, BackendLocator } from '@aws-amplify/platform-core'; import { DeployProps } from './cdk_deployer_singleton_factory.js'; import { CdkErrorMapper } from './cdk_error_mapper.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; @@ -269,12 +269,13 @@ void describe('invokeCDKCommand', () => { await assert.rejects( () => invoker.deploy(branchBackendId, sandboxDeployProps), - (err: Error) => { + (err: AmplifyError) => { assert.equal( err.message, - '[AccessDenied]: The deployment role does not have sufficient permissions to perform this deployment.' + 'The deployment role does not have sufficient permissions to perform this deployment.' ); - assert.equal((err.cause as Error).message, 'Access Denied'); + assert.equal(err.name, 'AccessDeniedError'); + assert.equal(err.cause?.message, 'Access Denied'); return true; } ); diff --git a/packages/backend-deployer/src/cdk_deployer.ts b/packages/backend-deployer/src/cdk_deployer.ts index 8ef8ce514c..f902455128 100644 --- a/packages/backend-deployer/src/cdk_deployer.ts +++ b/packages/backend-deployer/src/cdk_deployer.ts @@ -9,7 +9,11 @@ import { } from './cdk_deployer_singleton_factory.js'; import { CdkErrorMapper } from './cdk_error_mapper.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { BackendLocator, CDKContextKey } from '@aws-amplify/platform-core'; +import { + AmplifyUserError, + BackendLocator, + CDKContextKey, +} from '@aws-amplify/platform-core'; import { dirname } from 'path'; /** @@ -49,14 +53,18 @@ export class CDKDeployer implements BackendDeployer { } } - return this.invokeCdk(InvokableCommand.DEPLOY, backendId, cdkCommandArgs); + return this.tryInvokeCdk( + InvokableCommand.DEPLOY, + backendId, + cdkCommandArgs + ); }; /** * Invokes cdk destroy command */ destroy = async (backendId: BackendIdentifier) => { - return this.invokeCdk(InvokableCommand.DESTROY, backendId, ['--force']); + return this.tryInvokeCdk(InvokableCommand.DESTROY, backendId, ['--force']); }; private invokeTsc = async (deployProps?: DeployProps) => { @@ -78,14 +86,44 @@ export class CDKDeployer implements BackendDeployer { // If we cannot load ts config, turn off type checking return; } - await this.executeChildProcess('npx', [ - 'tsc', - '--noEmit', - '--skipLibCheck', - // pointing the project arg to the amplify backend directory will use the tsconfig present in that directory - '--project', - dirname(this.backendLocator.locate()), - ]); + try { + await this.executeChildProcess('npx', [ + 'tsc', + '--noEmit', + '--skipLibCheck', + // pointing the project arg to the amplify backend directory will use the tsconfig present in that directory + '--project', + dirname(this.backendLocator.locate()), + ]); + } catch (err) { + throw new AmplifyUserError( + 'SyntaxError', + { + message: + 'TypeScript validation check failed, check your backend definition', + }, + err instanceof Error ? err : undefined + ); + } + }; + + /** + * calls invokeCDK and wrap it in a try catch + */ + private tryInvokeCdk = async ( + invokableCommand: InvokableCommand, + backendId: BackendIdentifier, + additionalArguments?: string[] + ): Promise => { + try { + return await this.invokeCdk( + invokableCommand, + backendId, + additionalArguments + ); + } catch (err) { + throw this.cdkErrorMapper.getAmplifyError(err as Error); + } }; /** @@ -131,11 +169,7 @@ export class CDKDeployer implements BackendDeployer { cdkCommandArgs.push(...additionalArguments); } - try { - return await this.executeChildProcess('npx', cdkCommandArgs); - } catch (err) { - throw this.cdkErrorMapper.getHumanReadableError(err as Error); - } + return await this.executeChildProcess('npx', cdkCommandArgs); }; /** @@ -183,7 +217,10 @@ export class CDKDeployer implements BackendDeployer { await childProcess; return cdkOutput; } catch (error) { - // swallow execa error which is not really helpful, rather throw stderr + // swallow execa error which is most of the time noise (basically child exited with exit code...) + // bubbling this up to customers add confusion (Customers don't need to know we are running IPC calls + // and their exit codes printed while sandbox continue to run). Hence we explicitly don't pass error in the cause + // rather throw the entire stderr for clients to figure out what to do with it. throw new Error(aggregatedStderr); } }; diff --git a/packages/backend-deployer/src/cdk_error_mapper.test.ts b/packages/backend-deployer/src/cdk_error_mapper.test.ts index feacb5cf07..f1259a14a5 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.test.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.test.ts @@ -5,44 +5,76 @@ import { CdkErrorMapper } from './cdk_error_mapper.js'; const testErrorMappings = [ { errorMessage: 'UnknownError', - expectedTopLevelErrorMessage: 'UnknownError', + expectedTopLevelErrorMessage: 'Error: UnknownError', + errorName: 'UnknownFault', expectedDownstreamErrorMessage: undefined, }, { errorMessage: 'ExpiredToken', expectedTopLevelErrorMessage: - '[ExpiredToken]: The security token included in the request is invalid.', + 'The security token included in the request is invalid.', + errorName: 'ExpiredTokenError', expectedDownstreamErrorMessage: 'ExpiredToken', }, { errorMessage: 'Access Denied', expectedTopLevelErrorMessage: - '[AccessDenied]: The deployment role does not have sufficient permissions to perform this deployment.', + 'The deployment role does not have sufficient permissions to perform this deployment.', + errorName: 'AccessDeniedError', expectedDownstreamErrorMessage: 'Access Denied', }, + { + errorMessage: 'ReferenceError: var is not defined\n', + expectedTopLevelErrorMessage: + 'Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + errorName: 'SyntaxError', + expectedDownstreamErrorMessage: 'ReferenceError: var is not defined\n', + }, { errorMessage: 'Has the environment been bootstrapped', expectedTopLevelErrorMessage: - '[BootstrapFailure]: This AWS account and region has not been bootstrapped. Run `cdk bootstrap aws://{YOUR_ACCOUNT_ID}/{YOUR_REGION}` locally to resolve this.', + 'This AWS account and region has not been bootstrapped. Run `cdk bootstrap aws://{YOUR_ACCOUNT_ID}/{YOUR_REGION}` locally to resolve this.', + errorName: 'BootstrapNotDetectedError', expectedDownstreamErrorMessage: 'Has the environment been bootstrapped', }, + { + errorMessage: 'Amplify Backend not found in amplify/backend.ts', + expectedTopLevelErrorMessage: + 'Backend definition could not be found in amplify directory', + errorName: 'FileConventionError', + expectedDownstreamErrorMessage: + 'Amplify Backend not found in amplify/backend.ts', + }, + { + errorMessage: 'Amplify Auth must be defined in amplify/auth/resource.ts', + expectedTopLevelErrorMessage: + 'File name or path for backend definition are incorrect', + errorName: 'FileConventionError', + expectedDownstreamErrorMessage: + 'Amplify Auth must be defined in amplify/auth/resource.ts', + }, { errorMessage: 'amplify/backend.ts', expectedTopLevelErrorMessage: - '[SynthError]: Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + 'Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + errorName: 'BackendBuildError', expectedDownstreamErrorMessage: 'amplify/backend.ts', }, { - errorMessage: '❌ Deployment failed: something bad happened\n', + errorMessage: + 'Overall error message had other stuff before ❌ Deployment failed: something bad happened\n and after', expectedTopLevelErrorMessage: - '[CloudFormationFailure]: The CloudFormation deployment has failed. Find more information in the CloudFormation AWS Console for this stack.', - expectedDownstreamErrorMessage: 'something bad happened', + 'The CloudFormation deployment has failed. Find more information in the CloudFormation AWS Console for this stack.', + errorName: 'CloudFormationDeploymentError', + expectedDownstreamErrorMessage: + '❌ Deployment failed: something bad happened\n', }, { errorMessage: 'CFN error happened: Updates are not allowed for property: some property', expectedTopLevelErrorMessage: - '[UpdateNotSupported]: The changes that you are trying to apply are not supported.', + 'The changes that you are trying to apply are not supported.', + errorName: 'CFNUpdateNotSupportedError', expectedDownstreamErrorMessage: 'CFN error happened: Updates are not allowed for property: some property', }, @@ -50,7 +82,8 @@ const testErrorMappings = [ errorMessage: 'CFN error happened: Invalid AttributeDataType input, consider using the provided AttributeDataType enum', expectedTopLevelErrorMessage: - '[UpdateNotSupported]: User pool attributes cannot be changed after a user pool has been created.', + 'User pool attributes cannot be changed after a user pool has been created.', + errorName: 'CFNUpdateNotSupportedError', expectedDownstreamErrorMessage: 'CFN error happened: Invalid AttributeDataType input, consider using the provided AttributeDataType enum', }, @@ -62,16 +95,18 @@ void describe('invokeCDKCommand', { concurrency: 1 }, () => { ({ errorMessage, expectedTopLevelErrorMessage, + errorName: expectedErrorName, expectedDownstreamErrorMessage, }) => { void it(`handles ${errorMessage} error`, () => { - const humanReadableError = cdkErrorMapper.getHumanReadableError( + const humanReadableError = cdkErrorMapper.getAmplifyError( new Error(errorMessage) ); assert.equal(humanReadableError.message, expectedTopLevelErrorMessage); + assert.equal(humanReadableError.name, expectedErrorName); expectedDownstreamErrorMessage && assert.equal( - (humanReadableError.cause as Error).message, + humanReadableError.cause?.message, expectedDownstreamErrorMessage ); }); diff --git a/packages/backend-deployer/src/cdk_error_mapper.ts b/packages/backend-deployer/src/cdk_error_mapper.ts index c75189f47e..cf1fdd6685 100644 --- a/packages/backend-deployer/src/cdk_error_mapper.ts +++ b/packages/backend-deployer/src/cdk_error_mapper.ts @@ -1,41 +1,79 @@ +import { + AmplifyError, + AmplifyErrorClassification, + AmplifyErrorType, + AmplifyFault, + AmplifyLibraryFaultType, + AmplifyUserError, + AmplifyUserErrorType, +} from '@aws-amplify/platform-core'; + /** * Transforms CDK error messages to human readable ones */ export class CdkErrorMapper { private knownErrors: Array<{ errorRegex: RegExp; - humanReadableError: string; + humanReadableErrorMessage: string; + errorName: AmplifyErrorType; + classification: AmplifyErrorClassification; }> = [ { errorRegex: /ExpiredToken/, - humanReadableError: - '[ExpiredToken]: The security token included in the request is invalid.', + humanReadableErrorMessage: + 'The security token included in the request is invalid.', + errorName: 'ExpiredTokenError', + classification: 'ERROR', }, { errorRegex: /Access Denied/, - humanReadableError: - '[AccessDenied]: The deployment role does not have sufficient permissions to perform this deployment.', + humanReadableErrorMessage: + 'The deployment role does not have sufficient permissions to perform this deployment.', + errorName: 'AccessDeniedError', + classification: 'ERROR', }, { errorRegex: /Has the environment been bootstrapped/, - humanReadableError: - '[BootstrapFailure]: This AWS account and region has not been bootstrapped. Run `cdk bootstrap aws://{YOUR_ACCOUNT_ID}/{YOUR_REGION}` locally to resolve this.', + humanReadableErrorMessage: + 'This AWS account and region has not been bootstrapped. Run `cdk bootstrap aws://{YOUR_ACCOUNT_ID}/{YOUR_REGION}` locally to resolve this.', + errorName: 'BootstrapNotDetectedError', + classification: 'ERROR', }, { - // the backend entry point file is referenced in the stack indicating a problem in customer code - errorRegex: /amplify\/backend/, - humanReadableError: - '[SynthError]: Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + errorRegex: /(SyntaxError|ReferenceError):(.*)\n/, + humanReadableErrorMessage: + 'Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + errorName: 'SyntaxError', + classification: 'ERROR', + }, + { + errorRegex: /Amplify Backend not found in/, + humanReadableErrorMessage: + 'Backend definition could not be found in amplify directory', + errorName: 'FileConventionError', + classification: 'ERROR', + }, + { + errorRegex: /Amplify (.*) must be defined in (.*)/, + humanReadableErrorMessage: + 'File name or path for backend definition are incorrect', + errorName: 'FileConventionError', + classification: 'ERROR', }, { - errorRegex: /SyntaxError:(.*)\n/, - humanReadableError: - '[SyntaxError]: Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + // the backend entry point file is referenced in the stack indicating a problem in customer code + errorRegex: /amplify\/backend/, + humanReadableErrorMessage: + 'Unable to build Amplify backend. Check your backend definition in the `amplify` folder.', + errorName: 'BackendBuildError', + classification: 'ERROR', }, { errorRegex: /Updates are not allowed for property/, - humanReadableError: - '[UpdateNotSupported]: The changes that you are trying to apply are not supported.', + humanReadableErrorMessage: + 'The changes that you are trying to apply are not supported.', + errorName: 'CFNUpdateNotSupportedError', + classification: 'ERROR', }, { // This error originates from Cognito service when user tries to change UserPool attributes which is not allowed @@ -43,30 +81,52 @@ export class CdkErrorMapper { // Remapping to `UpdateNotSupported` will allow sandbox to prompt users for resetting their environment errorRegex: /Invalid AttributeDataType input, consider using the provided AttributeDataType enum/, - humanReadableError: - '[UpdateNotSupported]: User pool attributes cannot be changed after a user pool has been created.', + humanReadableErrorMessage: + 'User pool attributes cannot be changed after a user pool has been created.', + errorName: 'CFNUpdateNotSupportedError', + classification: 'ERROR', }, { // Note that the order matters, this should be the last as it captures generic CFN error errorRegex: /❌ Deployment failed: (.*)\n/, - humanReadableError: - '[CloudFormationFailure]: The CloudFormation deployment has failed. Find more information in the CloudFormation AWS Console for this stack.', + humanReadableErrorMessage: + 'The CloudFormation deployment has failed. Find more information in the CloudFormation AWS Console for this stack.', + errorName: 'CloudFormationDeploymentError', + classification: 'ERROR', }, ]; - getHumanReadableError = (error: Error): Error => { + getAmplifyError = (error: Error): AmplifyError => { + // Check if there was an Amplify error thrown during child process execution + const amplifyError = AmplifyError.fromStderr(error.message); + if (amplifyError) { + return amplifyError; + } + const matchingError = this.knownErrors.find((knownError) => knownError.errorRegex.test(error.message) ); if (matchingError) { + // Extract meaningful contextual information if available const underlyingMessage = error.message.match(matchingError.errorRegex); error.message = - underlyingMessage && underlyingMessage.length == 2 - ? underlyingMessage[1] + underlyingMessage && underlyingMessage.length > 1 + ? underlyingMessage[0] : error.message; - return new Error(matchingError.humanReadableError, { cause: error }); + + return matchingError.classification === 'ERROR' + ? new AmplifyUserError( + matchingError.errorName as AmplifyUserErrorType, + { message: matchingError.humanReadableErrorMessage }, + error + ) + : new AmplifyFault( + matchingError.errorName as AmplifyLibraryFaultType, + { message: matchingError.humanReadableErrorMessage }, + error + ); } - return error; + return AmplifyError.fromError(error); }; } diff --git a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts index 9f4d6d0588..d37afff008 100644 --- a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts +++ b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.test.ts @@ -1,5 +1,9 @@ import { ClientConfigFormat } from '@aws-amplify/client-config'; -import { UsageDataEmitter } from '@aws-amplify/platform-core'; +import { + AmplifyFault, + AmplifyUserError, + UsageDataEmitter, +} from '@aws-amplify/platform-core'; import assert from 'node:assert'; import { afterEach, describe, it, mock } from 'node:test'; import { ClientConfigGeneratorAdapter } from '../../client-config/client_config_generator_adapter.js'; @@ -82,8 +86,10 @@ void describe('sandbox_event_handler_factory', () => { }); }); - void it('calls the usage emitter on the failedDeployment event', async () => { - const testError = new Error('test message'); + void it('calls the usage emitter on the failedDeployment event with AmplifyError', async () => { + const testError = new AmplifyUserError('BackendBuildError', { + message: 'test message', + }); await Promise.all( eventFactory .getSandboxEventHandlers({ @@ -105,6 +111,49 @@ void describe('sandbox_event_handler_factory', () => { }); }); + void it('calls the usage emitter on the failedDeployment event with generic Error', async () => { + const testError = new Error('Some generic error'); + await Promise.all( + eventFactory + .getSandboxEventHandlers({ + sandboxName: 'my-app', + clientConfigLifecycleHandler, + }) + .failedDeployment.map((e) => e(testError)) + ); + + assert.deepStrictEqual(generateClientConfigMock.mock.callCount(), 0); + assert.strictEqual(emitSuccessMock.mock.callCount(), 0); + assert.strictEqual(emitFailureMock.mock.callCount(), 1); + + const expectedError = new AmplifyFault( + 'UnknownFault', + { + message: 'Error: Some generic error', + }, + testError + ); + assert.deepStrictEqual( + emitFailureMock.mock.calls[0].arguments[0].name, + expectedError.name + ); + assert.deepStrictEqual( + emitFailureMock.mock.calls[0].arguments[0].message, + expectedError.message + ); + assert.deepStrictEqual( + emitFailureMock.mock.calls[0].arguments[0].classification, + expectedError.classification + ); + assert.deepStrictEqual( + emitFailureMock.mock.calls[0].arguments[0].cause.message, + expectedError.cause?.message + ); + assert.deepStrictEqual(emitFailureMock.mock.calls[0].arguments[1], { + command: 'Sandbox', + }); + }); + void it('does not throw when client config adapter fails on the successfulDeployment event', async () => { generateClientConfigMock.mock.mockImplementationOnce(() => { throw new Error('test error message'); diff --git a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts index 2f121a6e2a..ad7e6124b9 100644 --- a/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts +++ b/packages/cli/src/commands/sandbox/sandbox_event_handler_factory.ts @@ -1,6 +1,6 @@ import { SandboxEventHandlerCreator } from './sandbox_command.js'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; -import { UsageDataEmitter } from '@aws-amplify/platform-core'; +import { AmplifyError, UsageDataEmitter } from '@aws-amplify/platform-core'; import { DeployResult } from '@aws-amplify/backend-deployer'; import { COLOR, Printer } from '@aws-amplify/cli-core'; @@ -73,11 +73,18 @@ export class SandboxEventHandlerFactory { if (args.length == 0 || !args[0]) { return; } - const deployError = args[0] as Error; - if (deployError && deployError.message) { + const deployError = args[0]; + if (deployError && deployError instanceof AmplifyError) { await usageDataEmitter.emitFailure(deployError, { command: 'Sandbox', }); + } else { + await usageDataEmitter.emitFailure( + AmplifyError.fromError(deployError), + { + command: 'Sandbox', + } + ); } }, ], diff --git a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index 28df51e0cc..b5e2c64de0 100644 --- a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -368,7 +368,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/bc335d45f5f3eaaf9af9b95d772b50f0586653ea00c896d3996ad6d0782d4f65.json" + "/356837bc006efa614dd18d8e0821efb2f05db80aadc347e28db04bb2f6fe8bbd.json" ] ] } @@ -421,7 +421,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/aa5980fc1cfd00374e5224df874d7414086ec4f45288dc19e5fa63133e85c9a0.json" + "/94663ad34627ee131e3d96f0aebd8bdccee2f3db588fc658564562b4eb233b1f.json" ] ] } diff --git a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json index 5d8b59a2c3..39d341963d 100644 --- a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json +++ b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.1\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.2\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", "Resources": { "amplifyAuthUserPool4BA7F805": { "Type": "AWS::Cognito::UserPool", diff --git a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json index a1838ed660..1196b355a6 100644 --- a/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json +++ b/packages/create-amplify/templates/basic-auth-data/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json @@ -47,7 +47,7 @@ "ApiId" ] }, - "Expires": 1703205067 + "Expires": 1703801369 } }, "amplifyDataGraphQLAPINONEDS684BF699": { diff --git a/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index 44f24ad523..9211743d83 100644 --- a/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -306,7 +306,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/0af95f79425d31eb2f7a183c84eac6fef81f8a45d5d1895224e37a513e96e9ac.json" + "/57f1f5883b3e09b18ae0032ff0869df039e028f57d981ec2043772d452221c5b.json" ] ] } diff --git a/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json b/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json index c3d3cbc8d1..d02044c679 100644 --- a/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json +++ b/packages/integration-tests/test-projects/data-iterative-deploy/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json @@ -31,7 +31,7 @@ "ApiId" ] }, - "Expires": 1701217852 + "Expires": 1701814158 } }, "amplifyDataGraphQLAPINONEDS684BF699": { diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index d1278d5bdb..6e19bd01a9 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -414,7 +414,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/5c41ca256392cb9a9c61aa6cedbae9fa9a12c5927d30678087511cbea6557158.json" + "/87aeaefe934ca0965957f8dd07a34567bd92cabf3305a2f27bf7d4da171a7b6f.json" ] ] } @@ -441,7 +441,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/34165810aaf3ae2c8e32368cda1337ca1d9242720102a6e34bc223e4f3f4ac94.json" + "/fffd6fccba429f67fbf54e07b891ec22c20a81177a051793869310e6e94b2a73.json" ] ] } @@ -468,7 +468,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/aae2917f7b6aa1fda37678972818215ed49129160005f03acfa90c29ad5f944f.json" + "/0d605cfcd689fb8650f00f193ce7572a75b06a8ccfaebc550876e3be9e352075.json" ] ] } diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json index da71ef18dd..81f31dbdab 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.1\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.2\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", "Resources": { "SecretFetcherResourceProviderLambdaServiceRole5ABAF823": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json index 94c1ca1f3b..efecf44653 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.0\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", "Resources": { "testFunctestFuncLambdaFunctionServiceRole09E19D41": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json index a89c3730fa..194423b7e3 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-cjs/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.2\",\"stackType\":\"storage-S3\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.3\",\"stackType\":\"storage-S3\",\"metadata\":{}}", "Resources": { "amplifyStorageamplifyStorageBucketC2F702CD": { "Type": "AWS::S3::Bucket", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index d1278d5bdb..6e19bd01a9 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -414,7 +414,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/5c41ca256392cb9a9c61aa6cedbae9fa9a12c5927d30678087511cbea6557158.json" + "/87aeaefe934ca0965957f8dd07a34567bd92cabf3305a2f27bf7d4da171a7b6f.json" ] ] } @@ -441,7 +441,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/34165810aaf3ae2c8e32368cda1337ca1d9242720102a6e34bc223e4f3f4ac94.json" + "/fffd6fccba429f67fbf54e07b891ec22c20a81177a051793869310e6e94b2a73.json" ] ] } @@ -468,7 +468,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/aae2917f7b6aa1fda37678972818215ed49129160005f03acfa90c29ad5f944f.json" + "/0d605cfcd689fb8650f00f193ce7572a75b06a8ccfaebc550876e3be9e352075.json" ] ] } diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json index da71ef18dd..81f31dbdab 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.1\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.2\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", "Resources": { "SecretFetcherResourceProviderLambdaServiceRole5ABAF823": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json index 94c1ca1f3b..efecf44653 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.0\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", "Resources": { "testFunctestFuncLambdaFunctionServiceRole09E19D41": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json index a89c3730fa..194423b7e3 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-js/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.2\",\"stackType\":\"storage-S3\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.3\",\"stackType\":\"storage-S3\",\"metadata\":{}}", "Resources": { "amplifyStorageamplifyStorageBucketC2F702CD": { "Type": "AWS::S3::Bucket", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index b1aa37e401..baf16dba0e 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -423,7 +423,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/5c41ca256392cb9a9c61aa6cedbae9fa9a12c5927d30678087511cbea6557158.json" + "/87aeaefe934ca0965957f8dd07a34567bd92cabf3305a2f27bf7d4da171a7b6f.json" ] ] } @@ -450,7 +450,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/34165810aaf3ae2c8e32368cda1337ca1d9242720102a6e34bc223e4f3f4ac94.json" + "/fffd6fccba429f67fbf54e07b891ec22c20a81177a051793869310e6e94b2a73.json" ] ] } @@ -477,7 +477,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/aae2917f7b6aa1fda37678972818215ed49129160005f03acfa90c29ad5f944f.json" + "/0d605cfcd689fb8650f00f193ce7572a75b06a8ccfaebc550876e3be9e352075.json" ] ] } @@ -536,7 +536,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/8cb0cb7e2fea755b319e74e97fe0b43998c9c837960f4b83af9a7c43f039b86c.json" + "/a2923c8e0151bf1cc187b890c02128997bb14abb8009bda4e67e9771b0e30c7a.json" ] ] } diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json index da71ef18dd..81f31dbdab 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854aauth473E022C.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.1\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.4.2\",\"stackType\":\"auth-Cognito\",\"metadata\":{}}", "Resources": { "SecretFetcherResourceProviderLambdaServiceRole5ABAF823": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json index a7cd02bcde..aad5172e57 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json @@ -47,7 +47,7 @@ "ApiId" ] }, - "Expires": 1703205062 + "Expires": 1703801364 } }, "amplifyDataGraphQLAPINONEDS684BF699": { diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json index 94c1ca1f3b..efecf44653 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.0\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", "Resources": { "testFunctestFuncLambdaFunctionServiceRole09E19D41": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json index a89c3730fa..194423b7e3 100644 --- a/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json +++ b/packages/integration-tests/test-projects/data-storage-auth-with-triggers-ts/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.2\",\"stackType\":\"storage-S3\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.3\",\"stackType\":\"storage-S3\",\"metadata\":{}}", "Resources": { "amplifyStorageamplifyStorageBucketC2F702CD": { "Type": "AWS::S3::Bucket", diff --git a/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index 6b6e5f9acf..a3c3041a35 100644 --- a/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -266,7 +266,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/aae2917f7b6aa1fda37678972818215ed49129160005f03acfa90c29ad5f944f.json" + "/0d605cfcd689fb8650f00f193ce7572a75b06a8ccfaebc550876e3be9e352075.json" ] ] } diff --git a/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json b/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json index a89c3730fa..194423b7e3 100644 --- a/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json +++ b/packages/integration-tests/test-projects/minimalist-project-with-typescript-idioms/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854astorage16B83955.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.2\",\"stackType\":\"storage-S3\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.3\",\"stackType\":\"storage-S3\",\"metadata\":{}}", "Resources": { "amplifyStorageamplifyStorageBucketC2F702CD": { "Type": "AWS::S3::Bucket", diff --git a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index fd607bc6ee..0661090f1d 100644 --- a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -318,7 +318,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/05251eaf9c7e228248013be4382298454e4222263c0e996131da678f06a468b3.json" + "/bbfa2151094c52f731c7da6f41f42b9fdbcffa26d8ab0aeb796923c8ad92e36f.json" ] ] } @@ -345,7 +345,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/1d9ba3324a466e898f90197e6412b5464de952663cd0619bbafedc38be06132d.json" + "/ff5f3459e2a88acce35d48501a8ec5378392604dbba56363c8c4153e4568f1f0.json" ] ] } diff --git a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json index c68287826f..fe64256218 100644 --- a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json +++ b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json @@ -51,7 +51,7 @@ "ApiId" ] }, - "Expires": 1703205064 + "Expires": 1703801366 } }, "amplifyDataGraphQLAPINONEDS684BF699": { diff --git a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json index 976873710c..6574a65285 100644 --- a/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json +++ b/packages/integration-tests/test-projects/standalone-data-auth-modes/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854afunctionDE2842E0.nested.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.0\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.2.1\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", "Resources": { "ApiAuthApiAuthLambdaFunctionServiceRole568E246E": { "Type": "AWS::IAM::Role", diff --git a/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json b/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json index b5d4fbddea..eeec0340bf 100644 --- a/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json +++ b/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplify-testAppId-testBranchName-branch-7d6f6c854a.template.json @@ -1,5 +1,5 @@ { - "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.4\",\"stackType\":\"root\",\"metadata\":{}}", + "Description": "{\"createdOn\":\"Mac\",\"createdBy\":\"AmplifyPipelineDeploy\",\"createdWith\":\"0.5.5\",\"stackType\":\"root\",\"metadata\":{}}", "Metadata": { "AWS::Amplify::Platform": { "version": "1", @@ -306,7 +306,7 @@ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, - "/426bd279acc51329d993502bd4f9aad03681287559c6b5c29cf4e4d8571e3f46.json" + "/17fc8d4466f74a8e2f3102e03c106d049ee22a25d86a9d47406f287011246496.json" ] ] } diff --git a/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json b/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json index 5f702208c6..0d3afa7817 100644 --- a/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json +++ b/packages/integration-tests/test-projects/standalone-data-sandbox-mode/expected-cdk-out/amplifytestAppIdtestBranchNamebranch7d6f6c854adataE67321C2.nested.template.json @@ -31,7 +31,7 @@ "ApiId" ] }, - "Expires": 1701217866 + "Expires": 1701814168 } }, "amplifyDataGraphQLAPINONEDS684BF699": { diff --git a/packages/platform-core/API.md b/packages/platform-core/API.md index 30228320e1..636b60d010 100644 --- a/packages/platform-core/API.md +++ b/packages/platform-core/API.md @@ -7,6 +7,64 @@ import { BackendIdentifier } from '@aws-amplify/plugin-types'; import z from 'zod'; +// @public +export abstract class AmplifyError extends Error { + constructor(name: AmplifyErrorType, classification: AmplifyErrorClassification, options: AmplifyErrorOptions, cause?: Error | undefined); + // (undocumented) + readonly cause?: Error | undefined; + // (undocumented) + readonly classification: AmplifyErrorClassification; + // (undocumented) + readonly code?: string; + // (undocumented) + readonly details?: string; + // (undocumented) + static fromError: (error: unknown) => AmplifyError; + // (undocumented) + static fromStderr: (_stderr: string) => AmplifyError | undefined; + // (undocumented) + readonly link?: string; + // (undocumented) + readonly message: string; + // (undocumented) + readonly name: AmplifyErrorType; + // (undocumented) + readonly resolution?: string; + // (undocumented) + serializedError?: string; +} + +// @public +export type AmplifyErrorClassification = 'FAULT' | 'ERROR'; + +// @public +export type AmplifyErrorOptions = { + message: string; + details?: string; + resolution?: string; + link?: string; + code?: string; +}; + +// @public +export type AmplifyErrorType = AmplifyUserErrorType | AmplifyLibraryFaultType; + +// @public +export class AmplifyFault extends AmplifyError { + constructor(name: AmplifyLibraryFaultType, options: AmplifyErrorOptions, cause?: Error); +} + +// @public +export type AmplifyLibraryFaultType = 'UnknownFault'; + +// @public +export class AmplifyUserError extends AmplifyError { + constructor(name: AmplifyUserErrorType, options: AmplifyErrorOptions, cause?: Error); +} + +// @public +export type AmplifyUserErrorType = 'InvalidSchemaAuthError' | 'InvalidSchemaError' | 'ExpiredTokenError' | 'CloudFormationDeploymentError' | 'CFNUpdateNotSupportedError' | 'SyntaxError' | 'BackendBuildError' | 'BootstrapNotDetectedError' | 'AccessDeniedError' | 'FileConventionError'; + // @public export class BackendIdentifierConversions { static fromStackName(stackName?: string): BackendIdentifier | undefined; @@ -88,7 +146,7 @@ export const USAGE_DATA_TRACKING_ENABLED = "telemetry.enabled"; // @public (undocumented) export type UsageDataEmitter = { emitSuccess: (metrics?: Record, dimensions?: Record) => Promise; - emitFailure: (error: Error, dimensions?: Record) => Promise; + emitFailure: (error: AmplifyError, dimensions?: Record) => Promise; }; // @public diff --git a/packages/platform-core/src/errors/amplify_error.test.ts b/packages/platform-core/src/errors/amplify_error.test.ts new file mode 100644 index 0000000000..27fcf8e897 --- /dev/null +++ b/packages/platform-core/src/errors/amplify_error.test.ts @@ -0,0 +1,28 @@ +import { describe, it } from 'node:test'; +import { AmplifyError, AmplifyUserError } from '.'; +import assert from 'assert'; +import * as util from 'util'; + +void describe('amplify error', () => { + void it('serialize and deserialize correctly', () => { + const testError = new AmplifyUserError( + 'SyntaxError', + { message: 'test error message', details: 'test error details' }, + new AmplifyUserError('AccessDeniedError', { + message: 'some downstream error message', + }) + ); + const sampleStderr = `some random stderr +before the actual error message +${util.inspect(testError, { depth: null })} +and some after the error message + `; + const actual = AmplifyError.fromStderr(sampleStderr); + assert.deepStrictEqual(actual?.name, testError.name); + assert.deepStrictEqual(actual?.classification, testError.classification); + assert.deepStrictEqual(actual?.message, testError.message); + assert.deepStrictEqual(actual?.details, testError.details); + assert.deepStrictEqual(actual?.cause?.name, testError.cause?.name); + assert.deepStrictEqual(actual?.cause?.message, testError.cause?.message); + }); +}); diff --git a/packages/platform-core/src/errors/amplify_error.ts b/packages/platform-core/src/errors/amplify_error.ts new file mode 100644 index 0000000000..92ad1698c3 --- /dev/null +++ b/packages/platform-core/src/errors/amplify_error.ts @@ -0,0 +1,133 @@ +import { AmplifyFault, AmplifyUserError } from '.'; + +/** + * Base class for all Amplify errors or faults + */ +export abstract class AmplifyError extends Error { + public serializedError?: string; + public readonly message: string; + public readonly resolution?: string; + public readonly details?: string; + public readonly link?: string; + public readonly code?: string; + + /** + * You should use AmplifyUserError or AmplifyLibraryFault to throw an error. + * @param name - a user friendly name for the exception + * @param classification - LibraryFault or UserError + * @param options - error stack, resolution steps, details, or help links + * @param cause If you are throwing this exception from within a catch block, + * you must provide the exception that was caught. + * @example + * try { + * ... + * } catch (error){ + * throw new AmplifyError(...,...,error); + * } + */ + constructor( + public readonly name: AmplifyErrorType, + public readonly classification: AmplifyErrorClassification, + private readonly options: AmplifyErrorOptions, + public readonly cause?: Error + ) { + // If an AmplifyError was already thrown, we must allow it to reach the user. + // This ensures that resolution steps, and the original error are bubbled up. + super(options.message, { cause }); + + // https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, AmplifyError.prototype); + + this.message = options.message; + this.details = options.details; + this.resolution = options.resolution; + this.code = options.code; + this.link = options.link; + + if (cause && cause instanceof AmplifyError) { + cause.serializedError = undefined; + } + this.serializedError = JSON.stringify({ + name, + classification, + options, + cause, + }); + } + + static fromStderr = (_stderr: string): AmplifyError | undefined => { + const extractionRegex = /["']?serializedError["']?:[ ]?["'](.*)["']/; + const serialized = _stderr.match(extractionRegex); + if (serialized && serialized.length == 2) { + try { + const { name, classification, options, cause } = JSON.parse( + serialized[1] + ); + return classification === 'ERROR' + ? new AmplifyUserError(name as AmplifyUserErrorType, options, cause) + : new AmplifyFault(name as AmplifyLibraryFaultType, options, cause); + } catch (error) { + // cannot deserialize + } + } + return undefined; + }; + + static fromError = (error: unknown): AmplifyError => { + const errorMessage = + error instanceof Error + ? `${error.name}: ${error.message}` + : 'An unknown error happened. Check downstream error'; + + return new AmplifyFault( + 'UnknownFault', + { + message: errorMessage, + }, + error instanceof Error ? error : new Error(String(error)) + ); + }; +} + +/** + * Amplify exception classifications + */ +export type AmplifyErrorClassification = 'FAULT' | 'ERROR'; + +/** + * Amplify Error options object + */ +export type AmplifyErrorOptions = { + message: string; + details?: string; + resolution?: string; + link?: string; + + // CloudFormation or NodeJS error codes + code?: string; +}; + +/** + * Amplify error types + */ +export type AmplifyErrorType = AmplifyUserErrorType | AmplifyLibraryFaultType; + +/** + * Amplify error types + */ +export type AmplifyUserErrorType = + | 'InvalidSchemaAuthError' + | 'InvalidSchemaError' + | 'ExpiredTokenError' + | 'CloudFormationDeploymentError' + | 'CFNUpdateNotSupportedError' + | 'SyntaxError' + | 'BackendBuildError' + | 'BootstrapNotDetectedError' + | 'AccessDeniedError' + | 'FileConventionError'; + +/** + * Amplify library fault types + */ +export type AmplifyLibraryFaultType = 'UnknownFault'; diff --git a/packages/platform-core/src/errors/amplify_library_fault.ts b/packages/platform-core/src/errors/amplify_library_fault.ts new file mode 100644 index 0000000000..5b2b7c6151 --- /dev/null +++ b/packages/platform-core/src/errors/amplify_library_fault.ts @@ -0,0 +1,31 @@ +import { + AmplifyError, + AmplifyErrorOptions, + AmplifyLibraryFaultType, +} from './amplify_error'; + +/** + * Base class for all Amplify library faults + */ +export class AmplifyFault extends AmplifyError { + /** + * Create a new Amplify Library Fault + * @param name - a user friendly name for the exception + * @param options - error stack, resolution steps, details, or help links + * @param cause If you are throwing this exception from within a catch block, + * you must provide the exception that was caught. + * @example + * try { + * ... + * } catch (error){ + * throw new AmplifyLibraryFault(error,...,...); + * } + */ + constructor( + name: AmplifyLibraryFaultType, + options: AmplifyErrorOptions, + cause?: Error + ) { + super(name, 'FAULT', options, cause); + } +} diff --git a/packages/platform-core/src/errors/amplify_user_error.ts b/packages/platform-core/src/errors/amplify_user_error.ts new file mode 100644 index 0000000000..e726c1237b --- /dev/null +++ b/packages/platform-core/src/errors/amplify_user_error.ts @@ -0,0 +1,31 @@ +import { + AmplifyError, + AmplifyErrorOptions, + AmplifyUserErrorType, +} from './amplify_error'; + +/** + * Base class for all Amplify user errors + */ +export class AmplifyUserError extends AmplifyError { + /** + * Create a new Amplify Error. + * @param name - a user friendly name for the user error + * @param options - error stack, resolution steps, details, or help links + * @param cause If you are throwing this error from within a catch block, + * you must provide the error that was caught. + * @example + * try { + * ... + * } catch (error){ + * throw new AmplifyError(...,...,error); + * } + */ + constructor( + name: AmplifyUserErrorType, + options: AmplifyErrorOptions, + cause?: Error + ) { + super(name, 'ERROR', options, cause); + } +} diff --git a/packages/platform-core/src/errors/index.ts b/packages/platform-core/src/errors/index.ts new file mode 100644 index 0000000000..03ddf6a6dd --- /dev/null +++ b/packages/platform-core/src/errors/index.ts @@ -0,0 +1,3 @@ +export * from './amplify_error'; +export * from './amplify_user_error'; +export * from './amplify_library_fault'; diff --git a/packages/platform-core/src/index.ts b/packages/platform-core/src/index.ts index d1980d40fc..9d87d45b26 100644 --- a/packages/platform-core/src/index.ts +++ b/packages/platform-core/src/index.ts @@ -4,5 +4,6 @@ export * from './extract_file_path_from_stack_trace_line.js'; export * from './package_json_reader.js'; export * from './usage-data/usage_data_emitter_factory.js'; export * from './config/local_configuration_controller_factory.js'; +export * from './errors'; export { USAGE_DATA_TRACKING_ENABLED } from './usage-data/constants.js'; export { CDKContextKey } from './cdk_context_key.js'; diff --git a/packages/platform-core/src/usage-data/serializable_error.ts b/packages/platform-core/src/usage-data/serializable_error.ts index c28e6f5223..75a997c6c4 100644 --- a/packages/platform-core/src/usage-data/serializable_error.ts +++ b/packages/platform-core/src/usage-data/serializable_error.ts @@ -4,9 +4,9 @@ import path from 'path'; * Wrapper around Error for serialization for usage metrics */ export class SerializableError { - stackTraceRegex = + private stackTraceRegex = /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i; - arnRegex = + private arnRegex = /arn:[a-z0-9][-.a-z0-9]{0,62}:[A-Za-z0-9][A-Za-z0-9_/.-]{0,62}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9_/.-]{0,63}:[A-Za-z0-9][A-Za-z0-9:_/+=,@.-]{0,1023}/g; name: string; @@ -19,7 +19,9 @@ export class SerializableError { */ constructor(error: Error) { this.name = - 'code' in error ? this.sanitize(error.code as string) : error.name; + 'code' in error && error.code + ? this.sanitize(error.code as string) + : error.name; this.message = this.sanitize(error.message); this.details = 'details' in error ? this.sanitize(error.details as string) : undefined; diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts index 67a8c4bef1..2353a1ce99 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.test.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.test.ts @@ -10,6 +10,7 @@ import os from 'os'; import { AccountIdFetcher } from './account_id_fetcher'; import { UsageData } from './usage_data'; import isCI from 'is-ci'; +import { AmplifyError, AmplifyUserError } from '..'; void describe('UsageDataEmitter', () => { let usageDataEmitter: DefaultUsageDataEmitter; @@ -78,7 +79,13 @@ void describe('UsageDataEmitter', () => { }); void test('happy case, emitFailure generates and send correct usage data', async () => { - const error = new Error('some error message'); + const error = new AmplifyUserError( + 'BackendBuildError', + { + message: 'some error message', + }, + new Error('some downstream exception') + ); await setupAndInvokeUsageEmitter({ isSuccess: false, error }); const usageDataSent: UsageData = JSON.parse( @@ -100,7 +107,9 @@ void describe('UsageDataEmitter', () => { assert.ok(validate(usageDataSent.sessionUuid)); assert.ok(validate(usageDataSent.installationUuid)); assert.strictEqual(usageDataSent.error?.message, 'some error message'); - assert.ok(usageDataSent.downstreamException == undefined); + assert.ok( + usageDataSent.downstreamException?.message == 'some downstream exception' + ); }); /** @@ -115,7 +124,7 @@ void describe('UsageDataEmitter', () => { */ const setupAndInvokeUsageEmitter = async (testData: { isSuccess: boolean; - error?: Error; + error?: AmplifyError; metrics?: Record; }) => { const reqEndHandlerAttached = new Promise((resolve) => { diff --git a/packages/platform-core/src/usage-data/usage_data_emitter.ts b/packages/platform-core/src/usage-data/usage_data_emitter.ts index 9d1b60443a..118ce69ecf 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter.ts @@ -9,6 +9,7 @@ import { getUrl } from './get_usage_data_url.js'; import isCI from 'is-ci'; import { SerializableError } from './serializable_error.js'; import { UsageDataEmitter } from './usage_data_emitter_factory.js'; +import { AmplifyError } from '../index.js'; /** * Entry point for sending usage data metrics @@ -36,7 +37,10 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { await this.send(data); }; - emitFailure = async (error: Error, dimensions?: Record) => { + emitFailure = async ( + error: AmplifyError, + dimensions?: Record + ) => { const data = await this.getUsageData({ state: 'FAILED', error, @@ -49,7 +53,7 @@ export class DefaultUsageDataEmitter implements UsageDataEmitter { state: 'SUCCEEDED' | 'FAILED'; metrics?: Record; dimensions?: Record; - error?: Error; + error?: AmplifyError; }) => { return { accountId: await this.accountIdFetcher.fetch(), diff --git a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts index 55dec214e8..6ee9a2fb11 100644 --- a/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts +++ b/packages/platform-core/src/usage-data/usage_data_emitter_factory.ts @@ -2,6 +2,7 @@ import { configControllerFactory } from '../config/local_configuration_controlle import { NoOpUsageDataEmitter } from './noop_usage_data_emitter.js'; import { DefaultUsageDataEmitter } from './usage_data_emitter.js'; import { USAGE_DATA_TRACKING_ENABLED } from './constants.js'; +import { AmplifyError } from '../index.js'; export type UsageDataEmitter = { emitSuccess: ( @@ -9,7 +10,7 @@ export type UsageDataEmitter = { dimensions?: Record ) => Promise; emitFailure: ( - error: Error, + error: AmplifyError, dimensions?: Record ) => Promise; }; @@ -22,7 +23,7 @@ export class UsageDataEmitterFactory { * Creates UsageDataEmitter for a given library version, usage data tracking preferences */ getInstance = async (libraryVersion: string): Promise => { - const configController = await configControllerFactory.getInstance( + const configController = configControllerFactory.getInstance( 'usage_data_preferences.json' ); diff --git a/packages/sandbox/src/file_watching_sandbox.test.ts b/packages/sandbox/src/file_watching_sandbox.test.ts index 0d7cfebcec..7ddb250efe 100644 --- a/packages/sandbox/src/file_watching_sandbox.test.ts +++ b/packages/sandbox/src/file_watching_sandbox.test.ts @@ -20,6 +20,7 @@ import { Sandbox } from './sandbox.js'; import { AmplifyPrompter } from '@aws-amplify/cli-core'; import { fileURLToPath } from 'url'; import { BackendIdentifier } from '@aws-amplify/plugin-types'; +import { AmplifyUserError } from '@aws-amplify/platform-core'; // Watcher mocks const unsubscribeMockFn = mock.fn(); @@ -512,7 +513,9 @@ void describe('Sandbox using local project name resolver', () => { 'deploy', () => Promise.reject( - new Error('[UpdateNotSupported] random BackendDeployer error') + new AmplifyUserError('CFNUpdateNotSupportedError', { + message: 'some error message', + }) ), { times: 1 } //mock implementation once ); @@ -552,7 +555,9 @@ void describe('Sandbox using local project name resolver', () => { 'deploy', () => Promise.reject( - new Error('[UpdateNotSupported] random BackendDeployer error') + new AmplifyUserError('CFNUpdateNotSupportedError', { + message: 'some error message', + }) ), { times: 1 } //mock implementation once ); diff --git a/packages/sandbox/src/file_watching_sandbox.ts b/packages/sandbox/src/file_watching_sandbox.ts index b98752c3e5..e6ebae7694 100644 --- a/packages/sandbox/src/file_watching_sandbox.ts +++ b/packages/sandbox/src/file_watching_sandbox.ts @@ -17,11 +17,12 @@ import { CloudFormationClient, DescribeStacksCommand, } from '@aws-sdk/client-cloudformation'; -import { AmplifyPrompter } from '@aws-amplify/cli-core'; +import { AmplifyPrompter, COLOR, Printer } from '@aws-amplify/cli-core'; import { FilesChangesTracker, createFilesChangesTracker, } from './files_changes_tracker.js'; +import { AmplifyError } from '@aws-amplify/platform-core'; export const CDK_BOOTSTRAP_STACK_NAME = 'CDKToolkit'; export const CDK_BOOTSTRAP_VERSION_KEY = 'BootstrapVersion'; @@ -212,16 +213,16 @@ export class FileWatchingSandbox extends EventEmitter implements Sandbox { console.debug('[Sandbox] Running successfulDeployment event handlers'); this.emit('successfulDeployment', deployResult); } catch (error) { - // Print the meaningful message - console.log(this.getErrorMessage(error)); + // Print a meaningful message + Printer.print(this.getErrorMessage(error), COLOR.RED); this.emit('failedDeployment', error); // If the error is because of a non-allowed destructive change such as // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-userpool.html#cfn-cognito-userpool-aliasattributes // offer to recreate the sandbox or revert the change if ( - error instanceof Error && - error.message.includes('UpdateNotSupported') + error instanceof AmplifyError && + error.name === 'CFNUpdateNotSupportedError' ) { await this.handleUnsupportedDestructiveChanges(options); }