Skip to content

Commit 2dc5d70

Browse files
authored
feat(docdb): throw ValidationErrors instead of untyped Errors (#33870)
### 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 6761f56 commit 2dc5d70

File tree

3 files changed

+20
-19
lines changed

3 files changed

+20
-19
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const enableNoThrowDefaultErrorIn = [
4646
'aws-codepipeline',
4747
'aws-codepipeline-actions',
4848
'aws-cognito',
49+
'aws-docdb',
4950
'aws-ecr',
5051
'aws-elasticloadbalancing',
5152
'aws-elasticloadbalancingv2',

packages/aws-cdk-lib/aws-docdb/lib/cluster.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as kms from '../../aws-kms';
1111
import * as logs from '../../aws-logs';
1212
import { CaCertificate } from '../../aws-rds';
1313
import * as secretsmanager from '../../aws-secretsmanager';
14-
import { CfnResource, Duration, RemovalPolicy, Resource, Token } from '../../core';
14+
import { CfnResource, Duration, RemovalPolicy, Resource, Token, UnscopedValidationError, ValidationError } from '../../core';
1515
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
1616

1717
const MIN_ENGINE_VERSION_FOR_IO_OPTIMIZED_STORAGE = 5;
@@ -355,35 +355,35 @@ export class DatabaseCluster extends DatabaseClusterBase {
355355

356356
public get instanceIdentifiers(): string[] {
357357
if (!this._instanceIdentifiers) {
358-
throw new Error('Cannot access `instanceIdentifiers` of an imported cluster without provided instanceIdentifiers');
358+
throw new UnscopedValidationError('Cannot access `instanceIdentifiers` of an imported cluster without provided instanceIdentifiers');
359359
}
360360
return this._instanceIdentifiers;
361361
}
362362

363363
public get clusterEndpoint(): Endpoint {
364364
if (!this._clusterEndpoint) {
365-
throw new Error('Cannot access `clusterEndpoint` of an imported cluster without an endpoint address and port');
365+
throw new UnscopedValidationError('Cannot access `clusterEndpoint` of an imported cluster without an endpoint address and port');
366366
}
367367
return this._clusterEndpoint;
368368
}
369369

370370
public get clusterReadEndpoint(): Endpoint {
371371
if (!this._clusterReadEndpoint) {
372-
throw new Error('Cannot access `clusterReadEndpoint` of an imported cluster without a readerEndpointAddress and port');
372+
throw new UnscopedValidationError('Cannot access `clusterReadEndpoint` of an imported cluster without a readerEndpointAddress and port');
373373
}
374374
return this._clusterReadEndpoint;
375375
}
376376

377377
public get instanceEndpoints(): Endpoint[] {
378378
if (!this._instanceEndpoints) {
379-
throw new Error('Cannot access `instanceEndpoints` of an imported cluster without instanceEndpointAddresses and port');
379+
throw new UnscopedValidationError('Cannot access `instanceEndpoints` of an imported cluster without instanceEndpointAddresses and port');
380380
}
381381
return this._instanceEndpoints;
382382
}
383383

384384
public get securityGroupId(): string {
385385
if (!this._securityGroupId) {
386-
throw new Error('Cannot access `securityGroupId` of an imported cluster without securityGroupId');
386+
throw new UnscopedValidationError('Cannot access `securityGroupId` of an imported cluster without securityGroupId');
387387
}
388388
return this._securityGroupId;
389389
}
@@ -479,7 +479,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
479479
// We cannot test whether the subnets are in different AZs, but at least we can test the amount.
480480
// See https://docs.aws.amazon.com/documentdb/latest/developerguide/replication.html#replication.high-availability
481481
if (subnetIds.length < 2) {
482-
throw new Error(`Cluster requires at least 2 subnets, got ${subnetIds.length}`);
482+
throw new ValidationError(`Cluster requires at least 2 subnets, got ${subnetIds.length}`, this);
483483
}
484484

485485
const subnetGroup = new CfnDBSubnetGroup(this, 'Subnets', {
@@ -497,7 +497,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
497497
vpc: this.vpc,
498498
});
499499
// HACK: Use an escape-hatch to apply a consistent removal policy to the
500-
// security group so we don't get errors when trying to delete the stack.
500+
// security group so we don't get ValidationErrors when trying to delete the stack.
501501
const securityGroupRemovalPolicy = this.getSecurityGroupRemovalPolicy(props);
502502
(securityGroup.node.defaultChild as CfnResource).applyRemovalPolicy(securityGroupRemovalPolicy, {
503503
applyToUpdateReplacePolicy: true,
@@ -529,20 +529,20 @@ export class DatabaseCluster extends DatabaseClusterBase {
529529
const storageEncrypted = props.storageEncrypted ?? true;
530530

531531
if (props.kmsKey && !storageEncrypted) {
532-
throw new Error('KMS key supplied but storageEncrypted is false');
532+
throw new ValidationError('KMS key supplied but storageEncrypted is false', this);
533533
}
534534

535535
const validEngineVersionRegex = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
536536
if (props.engineVersion !== undefined && !validEngineVersionRegex.test(props.engineVersion)) {
537-
throw new Error(`Invalid engine version: '${props.engineVersion}'. Engine version must be in the format x.y.z`);
537+
throw new ValidationError(`Invalid engine version: '${props.engineVersion}'. Engine version must be in the format x.y.z`, this);
538538
}
539539

540540
if (
541541
props.storageType === StorageType.IOPT1
542542
&& props.engineVersion !== undefined
543543
&& Number(props.engineVersion.split('.')[0]) < MIN_ENGINE_VERSION_FOR_IO_OPTIMIZED_STORAGE
544544
) {
545-
throw new Error(`I/O-optimized storage is supported starting with engine version 5.0.0, got '${props.engineVersion}'`);
545+
throw new ValidationError(`I/O-optimized storage is supported starting with engine version 5.0.0, got '${props.engineVersion}'`, this);
546546
}
547547

548548
// Create the DocDB cluster
@@ -594,7 +594,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
594594
// Create the instances
595595
const instanceCount = props.instances ?? DatabaseCluster.DEFAULT_NUM_INSTANCES;
596596
if (instanceCount < 1) {
597-
throw new Error('At least one instance is required');
597+
throw new ValidationError('At least one instance is required', this);
598598
}
599599

600600
const instanceRemovalPolicy = this.getInstanceRemovalPolicy(props);
@@ -651,7 +651,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
651651

652652
private getInstanceRemovalPolicy(props: DatabaseClusterProps) {
653653
if (props.instanceRemovalPolicy === RemovalPolicy.SNAPSHOT) {
654-
throw new Error('AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy');
654+
throw new ValidationError('AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy', this);
655655
}
656656
if (props.instanceRemovalPolicy) return props.instanceRemovalPolicy;
657657
return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
@@ -660,7 +660,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
660660

661661
private getSecurityGroupRemovalPolicy(props: DatabaseClusterProps) {
662662
if (props.securityGroupRemovalPolicy === RemovalPolicy.SNAPSHOT) {
663-
throw new Error('AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy');
663+
throw new ValidationError('AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy', this);
664664
}
665665
if (props.securityGroupRemovalPolicy) return props.securityGroupRemovalPolicy;
666666
return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ?
@@ -676,13 +676,13 @@ export class DatabaseCluster extends DatabaseClusterBase {
676676
@MethodMetadata()
677677
public addRotationSingleUser(automaticallyAfter?: Duration): secretsmanager.SecretRotation {
678678
if (!this.secret) {
679-
throw new Error('Cannot add single user rotation for a cluster without secret.');
679+
throw new ValidationError('Cannot add single user rotation for a cluster without secret.', this);
680680
}
681681

682682
const id = 'RotationSingleUser';
683683
const existing = this.node.tryFindChild(id);
684684
if (existing) {
685-
throw new Error('A single user rotation was already added to this cluster.');
685+
throw new ValidationError('A single user rotation was already added to this cluster.', this);
686686
}
687687

688688
return new secretsmanager.SecretRotation(this, id, {
@@ -702,7 +702,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
702702
@MethodMetadata()
703703
public addRotationMultiUser(id: string, options: RotationMultiUserOptions): secretsmanager.SecretRotation {
704704
if (!this.secret) {
705-
throw new Error('Cannot add multi user rotation for a cluster without secret.');
705+
throw new ValidationError('Cannot add multi user rotation for a cluster without secret.', this);
706706
}
707707
return new secretsmanager.SecretRotation(this, id, {
708708
secret: options.secret,

packages/aws-cdk-lib/aws-docdb/lib/endpoint.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Token } from '../../core';
1+
import { Token, UnscopedValidationError } from '../../core';
22

33
/**
44
* Connection endpoint of a database cluster or instance
@@ -52,7 +52,7 @@ export class Endpoint {
5252
*/
5353
constructor(address: string, port: number) {
5454
if (!Token.isUnresolved(port) && !Endpoint.isValidPort(port)) {
55-
throw new Error(`Port must be an integer between [${Endpoint.MIN_PORT}, ${Endpoint.MAX_PORT}] but got: ${port}`);
55+
throw new UnscopedValidationError(`Port must be an integer between [${Endpoint.MIN_PORT}, ${Endpoint.MAX_PORT}] but got: ${port}`);
5656
}
5757

5858
this.hostname = address;

0 commit comments

Comments
 (0)