Skip to content

Commit

Permalink
chore: wraps no outputs found error from backend output client (#2368)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amplifiyer authored Dec 30, 2024
1 parent 642b441 commit aaeda9b
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 186 deletions.
8 changes: 8 additions & 0 deletions .changeset/chilly-pillows-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@aws-amplify/model-generator': patch
'@aws-amplify/client-config': patch
'@aws-amplify/sandbox': patch
'@aws-amplify/backend-cli': patch
---

wraps no outputs found error from backend output client
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,60 @@ void describe('generate forms command', () => {
);
});

void it('throws user error if the stack outputs are undefined', async () => {
const fakeSandboxId = 'my-fake-app-my-fake-username';
const backendIdResolver = {
resolveDeployedBackendIdentifier: mock.fn(() =>
Promise.resolve({
namespace: fakeSandboxId,
name: fakeSandboxId,
type: 'sandbox',
})
),
resolveBackendIdentifier: mock.fn(() =>
Promise.resolve({
namespace: fakeSandboxId,
name: fakeSandboxId,
type: 'sandbox',
})
),
} as BackendIdentifierResolver;
const formGenerationHandler = new FormGenerationHandler({
awsClientProvider,
});

const fakedBackendOutputClient = {
getOutput: mock.fn(() => {
throw new BackendOutputClientError(
BackendOutputClientErrorType.NO_OUTPUTS_FOUND,
'stack outputs are undefined'
);
}),
};

const generateFormsCommand = new GenerateFormsCommand(
backendIdResolver,
() => fakedBackendOutputClient,
formGenerationHandler
);

const parser = yargs().command(
generateFormsCommand as unknown as CommandModule
);
const commandRunner = new TestCommandRunner(parser);
await assert.rejects(
() => commandRunner.runCommand('forms'),
(error: TestCommandError) => {
assert.strictEqual(error.error.name, 'AmplifyOutputsNotFoundError');
assert.strictEqual(
error.error.message,
'Amplify outputs not found in stack metadata'
);
return true;
}
);
});

void it('throws user error if credentials are expired when getting backend outputs', async () => {
const fakeSandboxId = 'my-fake-app-my-fake-username';
const backendIdResolver = {
Expand Down
114 changes: 57 additions & 57 deletions packages/cli/src/commands/generate/forms/generate_forms_command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,64 +82,64 @@ export class GenerateFormsCommand
try {
output = await backendOutputClient.getOutput(backendIdentifier);
} catch (error) {
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS
) {
throw new AmplifyUserError(
'DeploymentInProgressError',
{
message: 'Deployment is currently in progress.',
resolution: 'Re-run this command once the deployment completes.',
},
error
);
if (BackendOutputClientError.isBackendOutputClientError(error)) {
switch (error.code) {
case BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS:
throw new AmplifyUserError(
'DeploymentInProgressError',
{
message: 'Deployment is currently in progress.',
resolution:
'Re-run this command once the deployment completes.',
},
error
);
case BackendOutputClientErrorType.NO_STACK_FOUND:
throw new AmplifyUserError(
'StackDoesNotExistError',
{
message: 'Stack does not exist.',
resolution:
'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.',
},
error
);
case BackendOutputClientErrorType.NO_OUTPUTS_FOUND:
throw new AmplifyUserError(
'AmplifyOutputsNotFoundError',
{
message: 'Amplify outputs not found in stack metadata',
resolution: `Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists.
If this is a new sandbox or branch deployment, wait for the deployment to be successfully finished and try again.`,
},
error
);
case BackendOutputClientErrorType.CREDENTIALS_ERROR:
throw new AmplifyUserError(
'CredentialsError',
{
message:
'Unable to get backend outputs due to invalid credentials.',
resolution:
'Ensure your AWS credentials are correctly set and refreshed.',
},
error
);
case BackendOutputClientErrorType.ACCESS_DENIED:
throw new AmplifyUserError(
'AccessDeniedError',
{
message:
'Unable to get backend outputs due to insufficient permissions.',
resolution:
'Ensure you have permissions to call cloudformation:GetTemplateSummary.',
},
error
);
default:
throw error;
}
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.NO_STACK_FOUND
) {
throw new AmplifyUserError(
'StackDoesNotExistError',
{
message: 'Stack does not exist.',
resolution:
'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR
) {
throw new AmplifyUserError(
'CredentialsError',
{
message:
'Unable to get backend outputs due to invalid credentials.',
resolution:
'Ensure your AWS credentials are correctly set and refreshed.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.ACCESS_DENIED
) {
throw new AmplifyUserError(
'AccessDeniedError',
{
message:
'Unable to get backend outputs due to insufficient permissions.',
resolution:
'Ensure you have permissions to call cloudformation:GetTemplateSummary.',
},
error
);
}

throw error;
}

Expand Down
33 changes: 33 additions & 0 deletions packages/client-config/src/unified_client_config_generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,39 @@ void describe('UnifiedClientConfigGenerator', () => {
);
});

void it('throws user error if the stack outputs are undefined', async () => {
const outputRetrieval = mock.fn(() => {
throw new BackendOutputClientError(
BackendOutputClientErrorType.NO_OUTPUTS_FOUND,
'stack outputs are undefined'
);
});
const modelSchemaAdapter = new ModelIntrospectionSchemaAdapter(
stubClientProvider
);

const configContributors = new ClientConfigContributorFactory(
modelSchemaAdapter
).getContributors('1.3');

const clientConfigGenerator = new UnifiedClientConfigGenerator(
outputRetrieval,
configContributors
);

await assert.rejects(
() => clientConfigGenerator.generateClientConfig(),
(error: AmplifyUserError) => {
assert.strictEqual(
error.message,
'Amplify outputs not found in stack metadata'
);
assert.ok(error.resolution);
return true;
}
);
});

void it('throws user error if the stack is missing metadata', async () => {
const outputRetrieval = mock.fn(() => {
throw new BackendOutputClientError(
Expand Down
136 changes: 65 additions & 71 deletions packages/client-config/src/unified_client_config_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,78 +39,72 @@ export class UnifiedClientConfigGenerator implements ClientConfigGenerator {
try {
output = await this.fetchOutput();
} catch (error) {
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS
) {
throw new AmplifyUserError(
'DeploymentInProgressError',
{
message: 'Deployment is currently in progress.',
resolution: 'Re-run this command once the deployment completes.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.NO_STACK_FOUND
) {
throw new AmplifyUserError(
'StackDoesNotExistError',
{
message: 'Stack does not exist.',
resolution:
'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR
) {
throw new AmplifyUserError(
'NonAmplifyStackError',
{
message: 'Stack was not created with Amplify.',
resolution:
'Ensure the CloudFormation stack ID references a main stack created with Amplify, then re-run this command.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.CREDENTIALS_ERROR
) {
throw new AmplifyUserError(
'CredentialsError',
{
message:
'Unable to get backend outputs due to invalid credentials.',
resolution:
'Ensure your AWS credentials are correctly set and refreshed.',
},
error
);
}
if (
BackendOutputClientError.isBackendOutputClientError(error) &&
error.code === BackendOutputClientErrorType.ACCESS_DENIED
) {
throw new AmplifyUserError(
'AccessDeniedError',
{
message:
'Unable to get backend outputs due to insufficient permissions.',
resolution:
'Ensure you have permissions to call cloudformation:GetTemplateSummary.',
},
error
);
if (BackendOutputClientError.isBackendOutputClientError(error)) {
switch (error.code) {
case BackendOutputClientErrorType.DEPLOYMENT_IN_PROGRESS:
throw new AmplifyUserError(
'DeploymentInProgressError',
{
message: 'Deployment is currently in progress.',
resolution:
'Re-run this command once the deployment completes.',
},
error
);
case BackendOutputClientErrorType.NO_STACK_FOUND:
throw new AmplifyUserError(
'StackDoesNotExistError',
{
message: 'Stack does not exist.',
resolution:
'Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists, then re-run this command.',
},
error
);
case BackendOutputClientErrorType.METADATA_RETRIEVAL_ERROR:
throw new AmplifyUserError(
'NonAmplifyStackError',
{
message: 'Stack was not created with Amplify.',
resolution:
'Ensure the CloudFormation stack ID references a main stack created with Amplify, then re-run this command.',
},
error
);
case BackendOutputClientErrorType.NO_OUTPUTS_FOUND:
throw new AmplifyUserError(
'AmplifyOutputsNotFoundError',
{
message: 'Amplify outputs not found in stack metadata',
resolution: `Ensure the CloudFormation stack ID or Amplify App ID and branch specified are correct and exists.
If this is a new sandbox or branch deployment, wait for the deployment to be successfully finished and try again.`,
},
error
);
case BackendOutputClientErrorType.CREDENTIALS_ERROR:
throw new AmplifyUserError(
'CredentialsError',
{
message:
'Unable to get backend outputs due to invalid credentials.',
resolution:
'Ensure your AWS credentials are correctly set and refreshed.',
},
error
);
case BackendOutputClientErrorType.ACCESS_DENIED:
throw new AmplifyUserError(
'AccessDeniedError',
{
message:
'Unable to get backend outputs due to insufficient permissions.',
resolution:
'Ensure you have permissions to call cloudformation:GetTemplateSummary.',
},
error
);
}
}

throw error;
}
const backendOutput = unifiedBackendOutputSchema.parse(output);
Expand Down
Loading

0 comments on commit aaeda9b

Please sign in to comment.