Skip to content

Commit 1fea9f1

Browse files
authoredMar 17, 2025··
feat(logs): throw ValidationError instead of untyped Errors (#33753)
### 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 6a57c50 commit 1fea9f1

7 files changed

+25
-21
lines changed
 

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

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const enableNoThrowDefaultErrorIn = [
4747
'aws-elasticloadbalancingv2-actions',
4848
'aws-elasticloadbalancingv2-targets',
4949
'aws-lambda',
50+
'aws-logs',
5051
'aws-rds',
5152
'aws-s3',
5253
'aws-sns',

‎packages/aws-cdk-lib/aws-logs/lib/data-protection-policy.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Construct } from 'constructs';
22
import { ILogGroup } from './log-group';
33
import { IBucket } from '../../aws-s3';
4-
import { Stack } from '../../core';
4+
import { Stack, UnscopedValidationError } from '../../core';
55
/**
66
* Creates a data protection policy for CloudWatch Logs log groups.
77
*/
@@ -10,7 +10,7 @@ export class DataProtectionPolicy {
1010

1111
constructor(props: DataProtectionPolicyProps) {
1212
if (props.identifiers.length == 0) {
13-
throw new Error('DataIdentifier cannot be empty');
13+
throw new UnscopedValidationError('DataIdentifier cannot be empty');
1414
}
1515
this.dataProtectionPolicyProps = props;
1616
}

‎packages/aws-cdk-lib/aws-logs/lib/field-index-policy.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Construct } from 'constructs';
2+
import { UnscopedValidationError } from '../../core';
23

34
/**
45
* Creates a field index policy for CloudWatch Logs log groups.
@@ -8,7 +9,7 @@ export class FieldIndexPolicy {
89

910
constructor(props: FieldIndexPolicyProps) {
1011
if (props.fields.length > 20) {
11-
throw new Error('A maximum of 20 fields can be indexed per log group');
12+
throw new UnscopedValidationError('A maximum of 20 fields can be indexed per log group');
1213
}
1314
this.fieldIndexPolicyProps = props;
1415
}

‎packages/aws-cdk-lib/aws-logs/lib/log-group.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ILogSubscriptionDestination, SubscriptionFilter } from './subscription-
1010
import * as cloudwatch from '../../aws-cloudwatch';
1111
import * as iam from '../../aws-iam';
1212
import * as kms from '../../aws-kms';
13-
import { Annotations, Arn, ArnFormat, RemovalPolicy, Resource, Stack, Token } from '../../core';
13+
import { Annotations, Arn, ArnFormat, RemovalPolicy, Resource, Stack, Token, ValidationError } from '../../core';
1414
import { addConstructMetadata } from '../../core/lib/metadata-resource';
1515

1616
export interface ILogGroup extends iam.IResourceWithPolicy {
@@ -623,7 +623,7 @@ export class LogGroup extends LogGroupBase {
623623
if (retentionInDays === Infinity || retentionInDays === RetentionDays.INFINITE) { retentionInDays = undefined; }
624624

625625
if (retentionInDays !== undefined && !Token.isUnresolved(retentionInDays) && retentionInDays <= 0) {
626-
throw new Error(`retentionInDays must be positive, got ${retentionInDays}`);
626+
throw new ValidationError(`retentionInDays must be positive, got ${retentionInDays}`, this);
627627
}
628628

629629
let logGroupClass = props.logGroupClass;

‎packages/aws-cdk-lib/aws-logs/lib/metric-filter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { ILogGroup, MetricFilterOptions } from './log-group';
33
import { CfnMetricFilter } from './logs.generated';
44
import { Metric, MetricOptions } from '../../aws-cloudwatch';
5-
import { Resource } from '../../core';
5+
import { Resource, ValidationError } from '../../core';
66
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
77

88
/**
@@ -34,7 +34,7 @@ export class MetricFilter extends Resource {
3434

3535
const numberOfDimensions = Object.keys(props.dimensions ?? {}).length;
3636
if (numberOfDimensions > 3) {
37-
throw new Error(`MetricFilter only supports a maximum of 3 dimensions but received ${numberOfDimensions}.`);
37+
throw new ValidationError(`MetricFilter only supports a maximum of 3 dimensions but received ${numberOfDimensions}.`, this);
3838
}
3939

4040
// It looks odd to map this object to a singleton list, but that's how

‎packages/aws-cdk-lib/aws-logs/lib/pattern.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { UnscopedValidationError } from '../../core';
2+
13
// Implementation of metric patterns
24

35
/**
@@ -180,7 +182,7 @@ export class FilterPattern {
180182
* A JSON log pattern that matches if all given JSON log patterns match
181183
*/
182184
public static all(...patterns: JsonPattern[]): JsonPattern {
183-
if (patterns.length === 0) { throw new Error('Must supply at least one pattern, or use allEvents() to match all events.'); }
185+
if (patterns.length === 0) { throw new UnscopedValidationError('Must supply at least one pattern, or use allEvents() to match all events.'); }
184186
if (patterns.length === 1) { return patterns[0]; }
185187
return new JSONAggregatePattern('&&', patterns);
186188
}
@@ -189,7 +191,7 @@ export class FilterPattern {
189191
* A JSON log pattern that matches if any of the given JSON log patterns match
190192
*/
191193
public static any(...patterns: JsonPattern[]): JsonPattern {
192-
if (patterns.length === 0) { throw new Error('Must supply at least one pattern'); }
194+
if (patterns.length === 0) { throw new UnscopedValidationError('Must supply at least one pattern'); }
193195
if (patterns.length === 1) { return patterns[0]; }
194196
return new JSONAggregatePattern('||', patterns);
195197
}
@@ -282,7 +284,7 @@ class JSONPostfixPattern extends JsonPattern {
282284
class JSONAggregatePattern extends JsonPattern {
283285
public constructor(operator: string, patterns: JsonPattern[]) {
284286
if (operator !== '&&' && operator !== '||') {
285-
throw new Error('Operator must be one of && or ||');
287+
throw new UnscopedValidationError('Operator must be one of && or ||');
286288
}
287289

288290
const clauses = patterns.map(p => '(' + p.jsonPatternString + ')');
@@ -313,12 +315,12 @@ export class SpaceDelimitedTextPattern implements IFilterPattern {
313315
// going through the factory
314316
for (const column of columns) {
315317
if (!validColumnName(column)) {
316-
throw new Error(`Invalid column name: ${column}`);
318+
throw new UnscopedValidationError(`Invalid column name: ${column}`);
317319
}
318320
}
319321

320322
if (sum(columns.map(c => c === COL_ELLIPSIS ? 1 : 0)) > 1) {
321-
throw new Error("Can use at most one '...' column");
323+
throw new UnscopedValidationError("Can use at most one '...' column");
322324
}
323325

324326
return new SpaceDelimitedTextPattern(columns, {});
@@ -335,10 +337,10 @@ export class SpaceDelimitedTextPattern implements IFilterPattern {
335337
*/
336338
public whereString(columnName: string, comparison: string, value: string): SpaceDelimitedTextPattern {
337339
if (columnName === COL_ELLIPSIS) {
338-
throw new Error("Can't use '...' in a restriction");
340+
throw new UnscopedValidationError("Can't use '...' in a restriction");
339341
}
340342
if (this.columns.indexOf(columnName) === -1) {
341-
throw new Error(`Column in restrictions that is not in columns: ${columnName}`);
343+
throw new UnscopedValidationError(`Column in restrictions that is not in columns: ${columnName}`);
342344
}
343345

344346
comparison = validateStringOperator(comparison);
@@ -354,10 +356,10 @@ export class SpaceDelimitedTextPattern implements IFilterPattern {
354356
*/
355357
public whereNumber(columnName: string, comparison: string, value: number): SpaceDelimitedTextPattern {
356358
if (columnName === COL_ELLIPSIS) {
357-
throw new Error("Can't use '...' in a restriction");
359+
throw new UnscopedValidationError("Can't use '...' in a restriction");
358360
}
359361
if (this.columns.indexOf(columnName) === -1) {
360-
throw new Error(`Column in restrictions that is not in columns: ${columnName}`);
362+
throw new UnscopedValidationError(`Column in restrictions that is not in columns: ${columnName}`);
361363
}
362364

363365
comparison = validateNumericalOperator(comparison);
@@ -445,7 +447,7 @@ function validateStringOperator(operator: string) {
445447
if (operator === '==') { operator = '='; }
446448

447449
if (operator !== '=' && operator !== '!=') {
448-
throw new Error(`Invalid comparison operator ('${operator}'), must be either '=' or '!='`);
450+
throw new UnscopedValidationError(`Invalid comparison operator ('${operator}'), must be either '=' or '!='`);
449451
}
450452

451453
return operator;
@@ -463,7 +465,7 @@ function validateNumericalOperator(operator: string) {
463465
if (operator === '==') { operator = '='; }
464466

465467
if (VALID_OPERATORS.indexOf(operator) === -1) {
466-
throw new Error(`Invalid comparison operator ('${operator}'), must be one of ${VALID_OPERATORS.join(', ')}`);
468+
throw new UnscopedValidationError(`Invalid comparison operator ('${operator}'), must be one of ${VALID_OPERATORS.join(', ')}`);
467469
}
468470

469471
return operator;
@@ -478,7 +480,7 @@ function renderRestriction(column: string, restriction: ColumnRestriction) {
478480
} else if (restriction.stringValue) {
479481
return `${column} ${restriction.comparison} ${quoteTerm(restriction.stringValue)}`;
480482
} else {
481-
throw new Error('Invalid restriction');
483+
throw new UnscopedValidationError('Invalid restriction');
482484
}
483485
}
484486

‎packages/aws-cdk-lib/aws-logs/lib/subscription-filter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ILogGroup, SubscriptionFilterOptions } from './log-group';
33
import { CfnSubscriptionFilter } from './logs.generated';
44
import * as iam from '../../aws-iam';
55
import { KinesisDestination } from '../../aws-logs-destinations';
6-
import { Resource, Token } from '../../core';
6+
import { Resource, Token, ValidationError } from '../../core';
77
import { addConstructMetadata } from '../../core/lib/metadata-resource';
88

99
/**
@@ -67,7 +67,7 @@ export class SubscriptionFilter extends Resource {
6767
!Token.isUnresolved(props.destination) &&
6868
!(props.destination instanceof KinesisDestination)
6969
) {
70-
throw new Error('distribution property can only be used with KinesisDestination.');
70+
throw new ValidationError('distribution property can only be used with KinesisDestination.', this);
7171
}
7272

7373
const destProps = props.destination.bind(this, props.logGroup);

0 commit comments

Comments
 (0)
Please sign in to comment.