Skip to content

Commit cf63184

Browse files
authored
Merge branch 'main' into bobertzh/apigwv2-validation
2 parents 0d29662 + cc1988a commit cf63184

19 files changed

+125
-95
lines changed

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ 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',
2327
'aws-s3',
@@ -37,6 +41,14 @@ const enableNoThrowDefaultErrorIn = [
3741
'aws-route53recoverycontrol',
3842
'aws-route53recoveryreadiness',
3943
'aws-route53resolver',
44+
'aws-sns',
45+
'aws-sqs',
46+
'aws-ssm',
47+
'aws-ssmcontacts',
48+
'aws-ssmincidents',
49+
'aws-ssmquicksetup',
50+
'aws-synthetics',
51+
'aws-s3',
4052
'aws-s3-assets',
4153
'aws-s3-deployment',
4254
'aws-s3-notifications',

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

+5-4
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

+6-5
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

+2-1
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

+9-8
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
}

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

+25-24
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ITrustStore } from './trust-store';
99
import * as ec2 from '../../../aws-ec2';
1010
import * as cxschema from '../../../cloud-assembly-schema';
1111
import { Duration, FeatureFlags, Lazy, Resource, Token } from '../../../core';
12+
import { ValidationError } from '../../../core/lib/errors';
1213
import * as cxapi from '../../../cx-api';
1314
import { BaseListener, BaseListenerLookupOptions, IListener } from '../shared/base-listener';
1415
import { HealthCheck } from '../shared/base-target-group';
@@ -197,7 +198,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
197198
*/
198199
public static fromLookup(scope: Construct, id: string, options: ApplicationListenerLookupOptions): IApplicationListener {
199200
if (Token.isUnresolved(options.listenerArn)) {
200-
throw new Error('All arguments to look up a load balancer listener must be concrete (no Tokens)');
201+
throw new ValidationError('All arguments to look up a load balancer listener must be concrete (no Tokens)', scope);
201202
}
202203

203204
let listenerProtocol: cxschema.LoadBalancerListenerProtocol | undefined;
@@ -251,10 +252,10 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
251252
constructor(scope: Construct, id: string, props: ApplicationListenerProps) {
252253
const [protocol, port] = determineProtocolAndPort(props.protocol, props.port);
253254
if (protocol === undefined || port === undefined) {
254-
throw new Error('At least one of \'port\' or \'protocol\' is required');
255+
throw new ValidationError('At least one of \'port\' or \'protocol\' is required', scope);
255256
}
256257

257-
validateMutualAuthentication(props.mutualAuthentication);
258+
validateMutualAuthentication(scope, props.mutualAuthentication);
258259

259260
super(scope, id, {
260261
loadBalancerArn: props.loadBalancer.loadBalancerArn,
@@ -290,7 +291,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
290291
});
291292

292293
if (props.defaultAction && props.defaultTargetGroups) {
293-
throw new Error('Specify at most one of \'defaultAction\' and \'defaultTargetGroups\'');
294+
throw new ValidationError('Specify at most one of \'defaultAction\' and \'defaultTargetGroups\'', this);
294295
}
295296

296297
if (props.defaultAction) {
@@ -361,7 +362,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
361362
* default Action).
362363
*/
363364
public addAction(id: string, props: AddApplicationActionProps): void {
364-
checkAddRuleProps(props);
365+
checkAddRuleProps(this, props);
365366

366367
if (props.priority !== undefined) {
367368
// New rule
@@ -389,7 +390,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
389390
* become the default Action for this listener).
390391
*/
391392
public addTargetGroups(id: string, props: AddApplicationTargetGroupsProps): void {
392-
checkAddRuleProps(props);
393+
checkAddRuleProps(this, props);
393394

394395
if (props.priority !== undefined) {
395396
// New rule
@@ -423,7 +424,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
423424
public addTargets(id: string, props: AddApplicationTargetsProps): ApplicationTargetGroup {
424425
if (!this.loadBalancer.vpc) {
425426
// eslint-disable-next-line max-len
426-
throw new Error('Can only call addTargets() when using a constructed Load Balancer or an imported Load Balancer with specified vpc; construct a new TargetGroup and use addTargetGroup');
427+
throw new ValidationError('Can only call addTargets() when using a constructed Load Balancer or an imported Load Balancer with specified vpc; construct a new TargetGroup and use addTargetGroup', this);
427428
}
428429

429430
const group = new ApplicationTargetGroup(this, id + 'Group', {
@@ -445,7 +446,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
445446
* @deprecated Use `addAction()` instead
446447
*/
447448
public addFixedResponse(id: string, props: AddFixedResponseProps) {
448-
checkAddRuleProps(props);
449+
checkAddRuleProps(this, props);
449450

450451
const fixedResponse: FixedResponse = {
451452
statusCode: props.statusCode,
@@ -459,11 +460,11 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
459460
* Inlining the duplication functionality in v2 only (for now).
460461
*/
461462
if (fixedResponse.statusCode && !/^(2|4|5)\d\d$/.test(fixedResponse.statusCode)) {
462-
throw new Error('`statusCode` must be 2XX, 4XX or 5XX.');
463+
throw new ValidationError('`statusCode` must be 2XX, 4XX or 5XX.', this);
463464
}
464465

465466
if (fixedResponse.messageBody && fixedResponse.messageBody.length > 1024) {
466-
throw new Error('`messageBody` cannot have more than 1024 characters.');
467+
throw new ValidationError('`messageBody` cannot have more than 1024 characters.', this);
467468
}
468469

469470
if (props.priority) {
@@ -487,7 +488,7 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
487488
* @deprecated Use `addAction()` instead
488489
*/
489490
public addRedirectResponse(id: string, props: AddRedirectResponseProps) {
490-
checkAddRuleProps(props);
491+
checkAddRuleProps(this, props);
491492
const redirectResponse = {
492493
host: props.host,
493494
path: props.path,
@@ -503,11 +504,11 @@ export class ApplicationListener extends BaseListener implements IApplicationLis
503504
* Inlining the duplication functionality in v2 only (for now).
504505
*/
505506
if (redirectResponse.protocol && !/^(HTTPS?|#\{protocol\})$/i.test(redirectResponse.protocol)) {
506-
throw new Error('`protocol` must be HTTP, HTTPS, or #{protocol}.');
507+
throw new ValidationError('`protocol` must be HTTP, HTTPS, or #{protocol}.', this);
507508
}
508509

509510
if (!redirectResponse.statusCode || !/^HTTP_30[12]$/.test(redirectResponse.statusCode)) {
510-
throw new Error('`statusCode` must be HTTP_301 or HTTP_302.');
511+
throw new ValidationError('`statusCode` must be HTTP_301 or HTTP_302.', this);
511512
}
512513

513514
if (props.priority) {
@@ -698,7 +699,7 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat
698699
* At least one TargetGroup must be added without conditions.
699700
*/
700701
public addTargetGroups(id: string, props: AddApplicationTargetGroupsProps): void {
701-
checkAddRuleProps(props);
702+
checkAddRuleProps(this, props);
702703

703704
if (props.priority !== undefined) {
704705
// New rule
@@ -708,7 +709,7 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat
708709
...props,
709710
});
710711
} else {
711-
throw new Error('Cannot add default Target Groups to imported ApplicationListener');
712+
throw new ValidationError('Cannot add default Target Groups to imported ApplicationListener', this);
712713
}
713714
}
714715

@@ -725,7 +726,7 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat
725726
*/
726727
public addTargets(_id: string, _props: AddApplicationTargetsProps): ApplicationTargetGroup {
727728
// eslint-disable-next-line max-len
728-
throw new Error('Can only call addTargets() when using a constructed ApplicationListener; construct a new TargetGroup and use addTargetGroup.');
729+
throw new ValidationError('Can only call addTargets() when using a constructed ApplicationListener; construct a new TargetGroup and use addTargetGroup.', this);
729730
}
730731

731732
/**
@@ -747,7 +748,7 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat
747748
* property here to avoid having CloudFormation attempt to replace your resource.
748749
*/
749750
public addAction(id: string, props: AddApplicationActionProps): void {
750-
checkAddRuleProps(props);
751+
checkAddRuleProps(this, props);
751752

752753
if (props.priority !== undefined) {
753754
const ruleId = props.removeSuffix ? id : id + 'Rule';
@@ -760,7 +761,7 @@ abstract class ExternalApplicationListener extends Resource implements IApplicat
760761
...props,
761762
});
762763
} else {
763-
throw new Error('priority must be set for actions added to an imported listener');
764+
throw new ValidationError('priority must be set for actions added to an imported listener', this);
764765
}
765766
}
766767
}
@@ -1036,17 +1037,17 @@ export interface AddFixedResponseProps extends AddRuleProps, FixedResponse {
10361037
export interface AddRedirectResponseProps extends AddRuleProps, RedirectResponse {
10371038
}
10381039

1039-
function checkAddRuleProps(props: AddRuleProps) {
1040+
function checkAddRuleProps(scope: Construct, props: AddRuleProps) {
10401041
const conditionsCount = props.conditions?.length || 0;
10411042
const hasAnyConditions = conditionsCount !== 0 ||
10421043
props.hostHeader !== undefined || props.pathPattern !== undefined || props.pathPatterns !== undefined;
10431044
const hasPriority = props.priority !== undefined;
10441045
if (hasAnyConditions !== hasPriority) {
1045-
throw new Error('Setting \'conditions\', \'pathPattern\' or \'hostHeader\' also requires \'priority\', and vice versa');
1046+
throw new ValidationError('Setting \'conditions\', \'pathPattern\' or \'hostHeader\' also requires \'priority\', and vice versa', scope);
10461047
}
10471048
}
10481049

1049-
function validateMutualAuthentication(mutualAuthentication?: MutualAuthentication): void {
1050+
function validateMutualAuthentication(scope: Construct, mutualAuthentication?: MutualAuthentication): void {
10501051
if (!mutualAuthentication) {
10511052
return;
10521053
}
@@ -1055,17 +1056,17 @@ function validateMutualAuthentication(mutualAuthentication?: MutualAuthenticatio
10551056

10561057
if (currentMode === MutualAuthenticationMode.VERIFY) {
10571058
if (!mutualAuthentication.trustStore) {
1058-
throw new Error(`You must set 'trustStore' when 'mode' is '${MutualAuthenticationMode.VERIFY}'`);
1059+
throw new ValidationError(`You must set 'trustStore' when 'mode' is '${MutualAuthenticationMode.VERIFY}'`, scope);
10591060
}
10601061
}
10611062

10621063
if (currentMode === MutualAuthenticationMode.OFF || currentMode === MutualAuthenticationMode.PASS_THROUGH) {
10631064
if (mutualAuthentication.trustStore) {
1064-
throw new Error(`You cannot set 'trustStore' when 'mode' is '${MutualAuthenticationMode.OFF}' or '${MutualAuthenticationMode.PASS_THROUGH}'`);
1065+
throw new ValidationError(`You cannot set 'trustStore' when 'mode' is '${MutualAuthenticationMode.OFF}' or '${MutualAuthenticationMode.PASS_THROUGH}'`, scope);
10651066
}
10661067

10671068
if (mutualAuthentication.ignoreClientCertificateExpiry !== undefined) {
1068-
throw new Error(`You cannot set 'ignoreClientCertificateExpiry' when 'mode' is '${MutualAuthenticationMode.OFF}' or '${MutualAuthenticationMode.PASS_THROUGH}'`);
1069+
throw new ValidationError(`You cannot set 'ignoreClientCertificateExpiry' when 'mode' is '${MutualAuthenticationMode.OFF}' or '${MutualAuthenticationMode.PASS_THROUGH}'`, scope);
10691070
}
10701071
}
10711072
}

0 commit comments

Comments
 (0)