Skip to content

Commit cc1988a

Browse files
authored
feat(elbv2): throw ValidationError intsead of untyped errors (#33111)
### Issue `aws-elasticloadbalancing*` for #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 basically 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 b1dfdd2 commit cc1988a

19 files changed

+125
-103
lines changed

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,29 @@ baseConfig.rules['import/no-extraneous-dependencies'] = [
1616

1717
// no-throw-default-error
1818
const enableNoThrowDefaultErrorIn = [
19-
'aws-apigatewayv2-integrations',
2019
'aws-apigatewayv2-authorizers',
20+
'aws-apigatewayv2-integrations',
21+
'aws-elasticloadbalancing',
22+
'aws-elasticloadbalancingv2',
23+
'aws-elasticloadbalancingv2-actions',
24+
'aws-elasticloadbalancingv2-targets',
2125
'aws-lambda',
2226
'aws-rds',
23-
'aws-s3',
24-
'aws-sns',
25-
'aws-sqs',
26-
'aws-ssm',
27-
'aws-ssmcontacts',
28-
'aws-ssmincidents',
29-
'aws-ssmquicksetup',
30-
'aws-synthetics',
3127
'aws-route53',
3228
'aws-route53-patterns',
3329
'aws-route53-targets',
3430
'aws-route53profiles',
3531
'aws-route53recoverycontrol',
3632
'aws-route53recoveryreadiness',
3733
'aws-route53resolver',
34+
'aws-sns',
35+
'aws-sqs',
36+
'aws-ssm',
37+
'aws-ssmcontacts',
38+
'aws-ssmincidents',
39+
'aws-ssmquicksetup',
40+
'aws-synthetics',
41+
'aws-s3',
3842
'aws-s3-assets',
3943
'aws-s3-deployment',
4044
'aws-s3-notifications',

packages/aws-cdk-lib/aws-elasticloadbalancing/lib/load-balancer.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
SecurityGroup, SelectedSubnets, SubnetSelection, SubnetType,
66
} from '../../aws-ec2';
77
import { Duration, Lazy, Resource } from '../../core';
8+
import { ValidationError } from '../../core/lib/errors';
89

910
/**
1011
* Construction properties for a LoadBalancer
@@ -289,9 +290,9 @@ export class LoadBalancer extends Resource implements IConnectable {
289290
*/
290291
public addListener(listener: LoadBalancerListener): ListenerPort {
291292
if (listener.sslCertificateArn && listener.sslCertificateId) {
292-
throw new Error('"sslCertificateId" is deprecated, please use "sslCertificateArn" only.');
293+
throw new ValidationError('"sslCertificateId" is deprecated, please use "sslCertificateArn" only.', this);
293294
}
294-
const protocol = ifUndefinedLazy(listener.externalProtocol, () => wellKnownProtocol(listener.externalPort));
295+
const protocol = ifUndefinedLazy(listener.externalProtocol, () => wellKnownProtocol(this, listener.externalPort));
295296
const instancePort = listener.internalPort || listener.externalPort;
296297
const sslCertificateArn = listener.sslCertificateArn || listener.sslCertificateId;
297298
const instanceProtocol = ifUndefined(listener.internalProtocol,
@@ -449,10 +450,10 @@ export class ListenerPort implements IConnectable {
449450
}
450451
}
451452

452-
function wellKnownProtocol(port: number): LoadBalancingProtocol {
453+
function wellKnownProtocol(scope: Construct, port: number): LoadBalancingProtocol {
453454
const proto = tryWellKnownProtocol(port);
454455
if (!proto) {
455-
throw new Error(`Please supply protocol to go with port ${port}`);
456+
throw new ValidationError(`Please supply protocol to go with port ${port}`, scope);
456457
}
457458
return proto;
458459
}

packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/application-listener-action.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IApplicationListener } from './application-listener';
33
import { IApplicationTargetGroup } from './application-target-group';
44
import { Port } from '../../../aws-ec2';
55
import { Duration, SecretValue, Token, Tokenization } from '../../../core';
6+
import { UnscopedValidationError } from '../../../core/lib/errors';
67
import { CfnListener, CfnListenerRule } from '../elasticloadbalancingv2.generated';
78
import { IListenerAction } from '../shared/listener-action';
89

@@ -39,7 +40,7 @@ export class ListenerAction implements IListenerAction {
3940
*/
4041
public static forward(targetGroups: IApplicationTargetGroup[], options: ForwardOptions = {}): ListenerAction {
4142
if (targetGroups.length === 0) {
42-
throw new Error('Need at least one targetGroup in a ListenerAction.forward()');
43+
throw new UnscopedValidationError('Need at least one targetGroup in a ListenerAction.forward()');
4344
}
4445
if (targetGroups.length === 1 && options.stickinessDuration === undefined) {
4546
// Render a "simple" action for backwards compatibility with old templates
@@ -59,7 +60,7 @@ export class ListenerAction implements IListenerAction {
5960
*/
6061
public static weightedForward(targetGroups: WeightedTargetGroup[], options: ForwardOptions = {}): ListenerAction {
6162
if (targetGroups.length === 0) {
62-
throw new Error('Need at least one targetGroup in a ListenerAction.weightedForward()');
63+
throw new UnscopedValidationError('Need at least one targetGroup in a ListenerAction.weightedForward()');
6364
}
6465

6566
return new TargetGroupListenerAction(targetGroups.map(g => g.targetGroup), {
@@ -113,10 +114,10 @@ export class ListenerAction implements IListenerAction {
113114
*/
114115
public static redirect(options: RedirectOptions): ListenerAction {
115116
if ([options.host, options.path, options.port, options.protocol, options.query].findIndex(x => x !== undefined) === -1) {
116-
throw new Error('To prevent redirect loops, set at least one of \'protocol\', \'host\', \'port\', \'path\', or \'query\'.');
117+
throw new UnscopedValidationError('To prevent redirect loops, set at least one of \'protocol\', \'host\', \'port\', \'path\', or \'query\'.');
117118
}
118119
if (options.path && !Token.isUnresolved(options.path) && !options.path.startsWith('/')) {
119-
throw new Error(`Redirect path must start with a \'/\', got: ${options.path}`);
120+
throw new UnscopedValidationError(`Redirect path must start with a \'/\', got: ${options.path}`);
120121
}
121122

122123
return new ListenerAction({
@@ -200,7 +201,7 @@ export class ListenerAction implements IListenerAction {
200201
*/
201202
protected addRuleAction(actionJson: CfnListenerRule.ActionProperty) {
202203
if (this._actionJson) {
203-
throw new Error('rule action is already set');
204+
throw new UnscopedValidationError('rule action is already set');
204205
}
205206
this._actionJson = actionJson;
206207
}

packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/application-listener-certificate.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Construct } from 'constructs';
22
import { IApplicationListener } from './application-listener';
3+
import { ValidationError } from '../../../core/lib/errors';
34
import { CfnListenerCertificate } from '../elasticloadbalancingv2.generated';
45
import { IListenerCertificate } from '../shared/listener-certificate';
56

@@ -40,7 +41,7 @@ export class ApplicationListenerCertificate extends Construct {
4041
super(scope, id);
4142

4243
if (!props.certificateArns && !props.certificates) {
43-
throw new Error('At least one of \'certificateArns\' or \'certificates\' is required');
44+
throw new ValidationError('At least one of \'certificateArns\' or \'certificates\' is required', this);
4445
}
4546

4647
const certificates = [

packages/aws-cdk-lib/aws-elasticloadbalancingv2/lib/alb/application-listener-rule.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ListenerAction } from './application-listener-action';
44
import { IApplicationTargetGroup } from './application-target-group';
55
import { ListenerCondition } from './conditions';
66
import * as cdk from '../../../core';
7+
import { UnscopedValidationError, ValidationError } from '../../../core/lib/errors';
78
import { CfnListenerRule } from '../elasticloadbalancingv2.generated';
89
import { IListenerAction } from '../shared/listener-action';
910

@@ -216,17 +217,17 @@ export class ApplicationListenerRule extends Construct {
216217

217218
const hasPathPatterns = props.pathPatterns || props.pathPattern;
218219
if (this.conditions.length === 0 && !props.hostHeader && !hasPathPatterns) {
219-
throw new Error('At least one of \'conditions\', \'hostHeader\', \'pathPattern\' or \'pathPatterns\' is required when defining a load balancing rule.');
220+
throw new ValidationError('At least one of \'conditions\', \'hostHeader\', \'pathPattern\' or \'pathPatterns\' is required when defining a load balancing rule.', this);
220221
}
221222

222223
const possibleActions: Array<keyof ApplicationListenerRuleProps> = ['action', 'targetGroups', 'fixedResponse', 'redirectResponse'];
223224
const providedActions = possibleActions.filter(action => props[action] !== undefined);
224225
if (providedActions.length > 1) {
225-
throw new Error(`'${providedActions}' specified together, specify only one`);
226+
throw new ValidationError(`'${providedActions}' specified together, specify only one`, this);
226227
}
227228

228229
if (!cdk.Token.isUnresolved(props.priority) && props.priority <= 0) {
229-
throw new Error('Priority must have value greater than or equal to 1');
230+
throw new ValidationError('Priority must have value greater than or equal to 1', this);
230231
}
231232

232233
this.listener = props.listener;
@@ -244,7 +245,7 @@ export class ApplicationListenerRule extends Construct {
244245

245246
if (hasPathPatterns) {
246247
if (props.pathPattern && props.pathPatterns) {
247-
throw new Error('Both `pathPatterns` and `pathPattern` are specified, specify only one');
248+
throw new ValidationError('Both `pathPatterns` and `pathPattern` are specified, specify only one', this);
248249
}
249250
const pathPattern = props.pathPattern ? [props.pathPattern] : props.pathPatterns;
250251
this.setCondition('path-pattern', pathPattern);
@@ -393,11 +394,11 @@ export class ApplicationListenerRule extends Construct {
393394
*/
394395
function validateFixedResponse(fixedResponse: FixedResponse) {
395396
if (fixedResponse.statusCode && !/^(2|4|5)\d\d$/.test(fixedResponse.statusCode)) {
396-
throw new Error('`statusCode` must be 2XX, 4XX or 5XX.');
397+
throw new UnscopedValidationError('`statusCode` must be 2XX, 4XX or 5XX.');
397398
}
398399

399400
if (fixedResponse.messageBody && fixedResponse.messageBody.length > 1024) {
400-
throw new Error('`messageBody` cannot have more than 1024 characters.');
401+
throw new UnscopedValidationError('`messageBody` cannot have more than 1024 characters.');
401402
}
402403
}
403404

@@ -408,10 +409,10 @@ function validateFixedResponse(fixedResponse: FixedResponse) {
408409
*/
409410
function validateRedirectResponse(redirectResponse: RedirectResponse) {
410411
if (redirectResponse.protocol && !/^(HTTPS?|#\{protocol\})$/i.test(redirectResponse.protocol)) {
411-
throw new Error('`protocol` must be HTTP, HTTPS, or #{protocol}.');
412+
throw new UnscopedValidationError('`protocol` must be HTTP, HTTPS, or #{protocol}.');
412413
}
413414

414415
if (!redirectResponse.statusCode || !/^HTTP_30[12]$/.test(redirectResponse.statusCode)) {
415-
throw new Error('`statusCode` must be HTTP_301 or HTTP_302.');
416+
throw new UnscopedValidationError('`statusCode` must be HTTP_301 or HTTP_302.');
416417
}
417418
}

0 commit comments

Comments
 (0)