Skip to content

Commit 48f2bf7

Browse files
authored
feat(backup): throw ValidationError instead of untyped Errors (#33387)
### Issue Relates to #32569 ### Description of changes `ValidationErrors` everywhere ### Describe any new or updated permissions being added n/a ### Description of how you validated changes Existing tests. Exemptions granted as this is a refactor of existing code. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent b91965c commit 48f2bf7

File tree

4 files changed

+20
-19
lines changed

4 files changed

+20
-19
lines changed

packages/aws-cdk-lib/.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ baseConfig.rules['import/no-extraneous-dependencies'] = [
1616

1717
// no-throw-default-error
1818
const enableNoThrowDefaultErrorIn = [
19+
'aws-backup',
1920
'assets',
2021
'aws-amplify',
2122
'aws-amplifyuibuilder',

packages/aws-cdk-lib/aws-backup/lib/plan.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CfnBackupPlan } from './backup.generated';
33
import { BackupPlanCopyActionProps, BackupPlanRule } from './rule';
44
import { BackupSelection, BackupSelectionOptions } from './selection';
55
import { BackupVault, IBackupVault } from './vault';
6-
import { IResource, Lazy, Resource } from '../../core';
6+
import { IResource, Lazy, Resource, ValidationError } from '../../core';
77
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
88

99
/**
@@ -217,7 +217,7 @@ export class BackupPlan extends Resource implements IBackupPlan {
217217
public get backupVault(): IBackupVault {
218218
if (!this._backupVault) {
219219
// This cannot happen but is here to make TypeScript happy
220-
throw new Error('No backup vault!');
220+
throw new ValidationError('No backup vault!', this);
221221
}
222222

223223
return this._backupVault;

packages/aws-cdk-lib/aws-backup/lib/rule.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IBackupVault } from './vault';
22
import * as events from '../../aws-events';
3-
import { Duration, Token } from '../../core';
3+
import { Duration, Token, UnscopedValidationError } from '../../core';
44

55
/**
66
* Properties for a BackupPlanRule
@@ -206,30 +206,30 @@ export class BackupPlanRule {
206206
constructor(props: BackupPlanRuleProps) {
207207
if (props.deleteAfter && props.moveToColdStorageAfter &&
208208
props.deleteAfter.toDays() < props.moveToColdStorageAfter.toDays()) {
209-
throw new Error('`deleteAfter` must be greater than `moveToColdStorageAfter`');
209+
throw new UnscopedValidationError('`deleteAfter` must be greater than `moveToColdStorageAfter`');
210210
}
211211

212212
if (props.scheduleExpression && !/^cron/.test(props.scheduleExpression.expressionString)) {
213-
throw new Error('`scheduleExpression` must be of type `cron`');
213+
throw new UnscopedValidationError('`scheduleExpression` must be of type `cron`');
214214
}
215215

216216
const deleteAfter = (props.enableContinuousBackup && !props.deleteAfter) ? Duration.days(35) : props.deleteAfter;
217217

218218
if (props.enableContinuousBackup && props.moveToColdStorageAfter) {
219-
throw new Error('`moveToColdStorageAfter` must not be specified if `enableContinuousBackup` is enabled');
219+
throw new UnscopedValidationError('`moveToColdStorageAfter` must not be specified if `enableContinuousBackup` is enabled');
220220
}
221221

222222
if (props.enableContinuousBackup && props.deleteAfter &&
223223
(props.deleteAfter?.toDays() < 1 || props.deleteAfter?.toDays() > 35)) {
224-
throw new Error(`'deleteAfter' must be between 1 and 35 days if 'enableContinuousBackup' is enabled, but got ${props.deleteAfter.toHumanString()}`);
224+
throw new UnscopedValidationError(`'deleteAfter' must be between 1 and 35 days if 'enableContinuousBackup' is enabled, but got ${props.deleteAfter.toHumanString()}`);
225225
}
226226

227227
if (props.copyActions && props.copyActions.length > 0) {
228228
props.copyActions.forEach(copyAction => {
229229
if (copyAction.deleteAfter && !Token.isUnresolved(copyAction.deleteAfter) &&
230230
copyAction.moveToColdStorageAfter && !Token.isUnresolved(copyAction.moveToColdStorageAfter) &&
231231
copyAction.deleteAfter.toDays() < copyAction.moveToColdStorageAfter.toDays() + 90) {
232-
throw new Error([
232+
throw new UnscopedValidationError([
233233
'\'deleteAfter\' must at least 90 days later than corresponding \'moveToColdStorageAfter\'',
234234
`received 'deleteAfter: ${copyAction.deleteAfter.toDays()}' and 'moveToColdStorageAfter: ${copyAction.moveToColdStorageAfter.toDays()}'`,
235235
].join('\n'));

packages/aws-cdk-lib/aws-backup/lib/vault.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CfnBackupVault } from './backup.generated';
33
import * as iam from '../../aws-iam';
44
import * as kms from '../../aws-kms';
55
import * as sns from '../../aws-sns';
6-
import { ArnFormat, Duration, IResource, Lazy, Names, RemovalPolicy, Resource, Stack, Token } from '../../core';
6+
import { ArnFormat, Duration, IResource, Lazy, Names, RemovalPolicy, Resource, Stack, Token, ValidationError } from '../../core';
77
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
88

99
/**
@@ -209,7 +209,7 @@ abstract class BackupVaultBase extends Resource implements IBackupVault {
209209
public grant(grantee: iam.IGrantable, ...actions: string[]): iam.Grant {
210210
for (const action of actions) {
211211
if (action.indexOf('*') >= 0) {
212-
throw new Error("AWS Backup access policies don't support a wildcard in the Action key.");
212+
throw new ValidationError("AWS Backup access policies don't support a wildcard in the Action key.", this);
213213
}
214214
}
215215

@@ -246,10 +246,10 @@ export class BackupVault extends BackupVaultBase {
246246
const parsedArn = Stack.of(scope).splitArn(backupVaultArn, ArnFormat.COLON_RESOURCE_NAME);
247247

248248
if (parsedArn.arnFormat !== ArnFormat.COLON_RESOURCE_NAME) {
249-
throw new Error(`Backup Vault Arn ${backupVaultArn} has the wrong format, expected ${ArnFormat.COLON_RESOURCE_NAME}.`);
249+
throw new ValidationError(`Backup Vault Arn ${backupVaultArn} has the wrong format, expected ${ArnFormat.COLON_RESOURCE_NAME}.`, scope);
250250
}
251251
if (!parsedArn.resourceName) {
252-
throw new Error(`Backup Vault Arn ${backupVaultArn} does not have a resource name.`);
252+
throw new ValidationError(`Backup Vault Arn ${backupVaultArn} does not have a resource name.`, scope);
253253
}
254254

255255
class Import extends BackupVaultBase {
@@ -274,7 +274,7 @@ export class BackupVault extends BackupVaultBase {
274274
addConstructMetadata(this, props);
275275

276276
if (props.backupVaultName && !Token.isUnresolved(props.backupVaultName) && !/^[a-zA-Z0-9\-_]{2,50}$/.test(props.backupVaultName)) {
277-
throw new Error('Expected vault name to match pattern `^[a-zA-Z0-9\-_]{2,50}$`');
277+
throw new ValidationError('Expected vault name to match pattern `^[a-zA-Z0-9\-_]{2,50}$`', this);
278278
}
279279

280280
let notifications: CfnBackupVault.NotificationObjectTypeProperty | undefined;
@@ -296,7 +296,7 @@ export class BackupVault extends BackupVaultBase {
296296
accessPolicy: Lazy.any({ produce: () => this.accessPolicy.toJSON() }),
297297
encryptionKeyArn: props.encryptionKey && props.encryptionKey.keyArn,
298298
notifications,
299-
lockConfiguration: renderLockConfiguration(props.lockConfiguration),
299+
lockConfiguration: renderLockConfiguration(this, props.lockConfiguration),
300300
});
301301
vault.applyRemovalPolicy(props.removalPolicy);
302302

@@ -336,26 +336,26 @@ export class BackupVault extends BackupVaultBase {
336336
}
337337
}
338338

339-
function renderLockConfiguration(config?: LockConfiguration): CfnBackupVault.LockConfigurationTypeProperty | undefined {
339+
function renderLockConfiguration(scope: Construct, config?: LockConfiguration): CfnBackupVault.LockConfigurationTypeProperty | undefined {
340340
if (!config) {
341341
return undefined;
342342
}
343343

344344
if (config.changeableFor && config.changeableFor.toHours() < 72) {
345-
throw new Error(`AWS Backup enforces a 72-hour cooling-off period before Vault Lock takes effect and becomes immutable, got ${config.changeableFor.toHours()} hours`);
345+
throw new ValidationError(`AWS Backup enforces a 72-hour cooling-off period before Vault Lock takes effect and becomes immutable, got ${config.changeableFor.toHours()} hours`, scope);
346346
}
347347

348348
if (config.maxRetention) {
349349
if (config.maxRetention.toDays() > 36500) {
350-
throw new Error(`The longest maximum retention period you can specify is 36500 days, got ${config.maxRetention.toDays()} days`);
350+
throw new ValidationError(`The longest maximum retention period you can specify is 36500 days, got ${config.maxRetention.toDays()} days`, scope);
351351
}
352352
if (config.maxRetention.toDays() <= config.minRetention.toDays()) {
353-
throw new Error(`The maximum retention period (${config.maxRetention.toDays()} days) must be greater than the minimum retention period (${config.minRetention.toDays()} days)`);
353+
throw new ValidationError(`The maximum retention period (${config.maxRetention.toDays()} days) must be greater than the minimum retention period (${config.minRetention.toDays()} days)`, scope);
354354
}
355355
}
356356

357357
if (config.minRetention.toHours() < 24) {
358-
throw new Error(`The shortest minimum retention period you can specify is 1 day, got ${config.minRetention.toHours()} hours`);
358+
throw new ValidationError(`The shortest minimum retention period you can specify is 1 day, got ${config.minRetention.toHours()} hours`, scope);
359359
}
360360

361361
return {

0 commit comments

Comments
 (0)