Skip to content

Commit 242690f

Browse files
feat(ecr): throw ValidationError instead of untyped Errors (#33750)
### Issue # (if applicable) Relates to #32569 ### Reason for this change untyped Errors are not recommended ### Description of changes ValidationErrors everywhere ### Describe any new or updated permissions being added none ### 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 a64b01c commit 242690f

File tree

2 files changed

+18
-15
lines changed

2 files changed

+18
-15
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const enableNoThrowDefaultErrorIn = [
4141
'aws-cloudtrail',
4242
'aws-cloudwatch',
4343
'aws-cloudwatch-actions',
44+
'aws-ecr',
4445
'aws-elasticloadbalancing',
4546
'aws-elasticloadbalancingv2',
4647
'aws-elasticloadbalancingv2-actions',

packages/aws-cdk-lib/aws-ecr/lib/repository.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
TokenComparison,
1919
CustomResource,
2020
Aws,
21+
ValidationError,
22+
UnscopedValidationError,
2123
} from '../../core';
2224
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
2325
import { AutoDeleteImagesProvider } from '../../custom-resource-handlers/dist/aws-ecr/auto-delete-images-provider.generated';
@@ -630,7 +632,7 @@ export class Repository extends RepositoryBase {
630632
// repository names can include "/" (e.g. foo/bar/myrepo) and it is impossible to
631633
// parse the name from an ARN using CloudFormation's split/select.
632634
if (Token.isUnresolved(repositoryArn)) {
633-
throw new Error('"repositoryArn" is a late-bound value, and therefore "repositoryName" is required. Use `fromRepositoryAttributes` instead');
635+
throw new UnscopedValidationError('"repositoryArn" is a late-bound value, and therefore "repositoryName" is required. Use `fromRepositoryAttributes` instead');
634636
}
635637

636638
validateRepositoryArn();
@@ -655,7 +657,7 @@ export class Repository extends RepositoryBase {
655657
const splitArn = repositoryArn.split(':');
656658

657659
if (!splitArn[splitArn.length - 1].startsWith('repository/')) {
658-
throw new Error(`Repository arn should be in the format 'arn:<PARTITION>:ecr:<REGION>:<ACCOUNT>:repository/<NAME>', got ${repositoryArn}.`);
660+
throw new UnscopedValidationError(`Repository arn should be in the format 'arn:<PARTITION>:ecr:<REGION>:<ACCOUNT>:repository/<NAME>', got ${repositoryArn}.`);
659661
}
660662
}
661663
}
@@ -707,7 +709,7 @@ export class Repository extends RepositoryBase {
707709
}
708710

709711
if (errors.length > 0) {
710-
throw new Error(`Invalid ECR repository name (value: ${repositoryName})${EOL}${errors.join(EOL)}`);
712+
throw new UnscopedValidationError(`Invalid ECR repository name (value: ${repositoryName})${EOL}${errors.join(EOL)}`);
711713
}
712714
}
713715

@@ -754,10 +756,10 @@ export class Repository extends RepositoryBase {
754756
});
755757

756758
if (props.emptyOnDelete && props.removalPolicy !== RemovalPolicy.DESTROY) {
757-
throw new Error('Cannot use \'emptyOnDelete\' property on a repository without setting removal policy to \'DESTROY\'.');
759+
throw new ValidationError('Cannot use \'emptyOnDelete\' property on a repository without setting removal policy to \'DESTROY\'.', this);
758760
} else if (props.emptyOnDelete == undefined && props.autoDeleteImages) {
759761
if (props.removalPolicy !== RemovalPolicy.DESTROY) {
760-
throw new Error('Cannot use \'autoDeleteImages\' property on a repository without setting removal policy to \'DESTROY\'.');
762+
throw new ValidationError('Cannot use \'autoDeleteImages\' property on a repository without setting removal policy to \'DESTROY\'.', this);
761763
}
762764
this.enableAutoDeleteImages();
763765
}
@@ -801,28 +803,28 @@ export class Repository extends RepositoryBase {
801803
&& (rule.tagPrefixList === undefined || rule.tagPrefixList.length === 0)
802804
&& (rule.tagPatternList === undefined || rule.tagPatternList.length === 0)
803805
) {
804-
throw new Error('TagStatus.Tagged requires the specification of a tagPrefixList or a tagPatternList');
806+
throw new ValidationError('TagStatus.Tagged requires the specification of a tagPrefixList or a tagPatternList', this);
805807
}
806808
if (rule.tagStatus !== TagStatus.TAGGED && (rule.tagPrefixList !== undefined || rule.tagPatternList !== undefined)) {
807-
throw new Error('tagPrefixList and tagPatternList can only be specified when tagStatus is set to Tagged');
809+
throw new ValidationError('tagPrefixList and tagPatternList can only be specified when tagStatus is set to Tagged', this);
808810
}
809811
if (rule.tagPrefixList !== undefined && rule.tagPatternList !== undefined) {
810-
throw new Error('Both tagPrefixList and tagPatternList cannot be specified together in a rule');
812+
throw new ValidationError('Both tagPrefixList and tagPatternList cannot be specified together in a rule', this);
811813
}
812814
if (rule.tagPatternList !== undefined) {
813815
rule.tagPatternList.forEach((pattern) => {
814816
const splitPatternLength = pattern.split('*').length;
815817
if (splitPatternLength > 5) {
816-
throw new Error(`A tag pattern cannot contain more than four wildcard characters (*), pattern: ${pattern}, counts: ${splitPatternLength - 1}`);
818+
throw new ValidationError(`A tag pattern cannot contain more than four wildcard characters (*), pattern: ${pattern}, counts: ${splitPatternLength - 1}`, this);
817819
}
818820
});
819821
}
820822
if ((rule.maxImageAge !== undefined) === (rule.maxImageCount !== undefined)) {
821-
throw new Error(`Life cycle rule must contain exactly one of 'maxImageAge' and 'maxImageCount', got: ${JSON.stringify(rule)}`);
823+
throw new ValidationError(`Life cycle rule must contain exactly one of 'maxImageAge' and 'maxImageCount', got: ${JSON.stringify(rule)}`, this);
822824
}
823825

824826
if (rule.tagStatus === TagStatus.ANY && this.lifecycleRules.filter(r => r.tagStatus === TagStatus.ANY).length > 0) {
825-
throw new Error('Life cycle can only have one TagStatus.Any rule');
827+
throw new ValidationError('Life cycle can only have one TagStatus.Any rule', this);
826828
}
827829

828830
this.lifecycleRules.push({ ...rule });
@@ -862,7 +864,7 @@ export class Repository extends RepositoryBase {
862864
const anyRules = this.lifecycleRules.filter(r => r.tagStatus === TagStatus.ANY);
863865
if (anyRules.length > 0 && anyRules[0].rulePriority !== undefined && autoPrioritizedRules.length > 0) {
864866
// Supporting this is too complex for very little value. We just prohibit it.
865-
throw new Error("Cannot combine prioritized TagStatus.Any rule with unprioritized rules. Remove rulePriority from the 'Any' rule.");
867+
throw new ValidationError("Cannot combine prioritized TagStatus.Any rule with unprioritized rules. Remove rulePriority from the 'Any' rule.", this);
866868
}
867869

868870
const prios = prioritizedRules.map(r => r.rulePriority!);
@@ -891,7 +893,7 @@ export class Repository extends RepositoryBase {
891893

892894
// if encryption key is set, encryption must be set to KMS.
893895
if (encryptionType !== RepositoryEncryption.KMS && props.encryptionKey) {
894-
throw new Error(`encryptionKey is specified, so 'encryption' must be set to KMS (value: ${encryptionType.value})`);
896+
throw new ValidationError(`encryptionKey is specified, so 'encryption' must be set to KMS (value: ${encryptionType.value})`, this);
895897
}
896898

897899
if (encryptionType === RepositoryEncryption.AES_256) {
@@ -905,7 +907,7 @@ export class Repository extends RepositoryBase {
905907
};
906908
}
907909

908-
throw new Error(`Unexpected 'encryptionType': ${encryptionType}`);
910+
throw new ValidationError(`Unexpected 'encryptionType': ${encryptionType}`, this);
909911
}
910912

911913
private enableAutoDeleteImages() {
@@ -958,7 +960,7 @@ function validateAnyRuleLast(rules: LifecycleRule[]) {
958960
if (anyRules.length === 1) {
959961
const maxPrio = Math.max(...rules.map(r => r.rulePriority!));
960962
if (anyRules[0].rulePriority !== maxPrio) {
961-
throw new Error(`TagStatus.Any rule must have highest priority, has ${anyRules[0].rulePriority} which is smaller than ${maxPrio}`);
963+
throw new UnscopedValidationError(`TagStatus.Any rule must have highest priority, has ${anyRules[0].rulePriority} which is smaller than ${maxPrio}`);
962964
}
963965
}
964966
}

0 commit comments

Comments
 (0)