Skip to content

Commit bc7c4e8

Browse files
authored
feat(scheduler): throw ValidationErrors instead of untyped errors (#34434)
### Issue Relates to #32569 ### Reason for this change untyped Errors are not recommended ### Description of changes `ValidationError`s 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 13d9645 commit bc7c4e8

File tree

9 files changed

+26
-26
lines changed

9 files changed

+26
-26
lines changed

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

-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ const noThrowDefaultErrorNotYetSupported = [
2525
'aws-lambda-destinations',
2626
'aws-lambda-event-sources',
2727
'aws-lambda-nodejs',
28-
'aws-scheduler-targets',
29-
'aws-scheduler',
3028
'aws-secretsmanager',
3129
'aws-servicecatalog',
3230
'core',

packages/aws-cdk-lib/aws-scheduler-targets/lib/event-bridge-put-events.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
22
import * as events from '../../aws-events';
33
import { IRole } from '../../aws-iam';
44
import { IScheduleTarget, ISchedule, ScheduleTargetInput, ScheduleTargetConfig } from '../../aws-scheduler';
5+
import { UnscopedValidationError } from '../../core';
56

67
/**
78
* An entry to be sent to EventBridge
@@ -54,7 +55,7 @@ export class EventBridgePutEvents extends ScheduleTargetBase implements ISchedul
5455
) {
5556
super(props, entry.eventBus.eventBusArn);
5657
if (this.props.input) {
57-
throw new Error('ScheduleTargetBaseProps.input is not supported for EventBridgePutEvents. Please use entry.detail instead.');
58+
throw new UnscopedValidationError('ScheduleTargetBaseProps.input is not supported for EventBridgePutEvents. Please use entry.detail instead.');
5859
}
5960
}
6061

packages/aws-cdk-lib/aws-scheduler-targets/lib/kinesis-stream-put-record.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
22
import { IRole } from '../../aws-iam';
33
import * as kinesis from '../../aws-kinesis';
44
import { ISchedule, IScheduleTarget, ScheduleTargetConfig } from '../../aws-scheduler';
5-
import { Token } from '../../core';
5+
import { Token, ValidationError } from '../../core';
66

77
/**
88
* Properties for a Kinesis Data Streams Target
@@ -29,7 +29,7 @@ export class KinesisStreamPutRecord extends ScheduleTargetBase implements ISched
2929
super(props, stream.streamArn);
3030

3131
if (!Token.isUnresolved(props.partitionKey) && (props.partitionKey.length < 1 || props.partitionKey.length > 256)) {
32-
throw new Error(`partitionKey length must be between 1 and 256, got ${props.partitionKey.length}`);
32+
throw new ValidationError(`partitionKey length must be between 1 and 256, got ${props.partitionKey.length}`, stream);
3333
}
3434
}
3535

packages/aws-cdk-lib/aws-scheduler-targets/lib/sage-maker-start-pipeline-execution.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
22
import { IRole } from '../../aws-iam';
33
import { IPipeline } from '../../aws-sagemaker';
44
import { ISchedule, IScheduleTarget, ScheduleTargetConfig } from '../../aws-scheduler';
5+
import { ValidationError } from '../../core';
56

67
/**
78
* Properties for a pipeline parameter
@@ -45,7 +46,7 @@ export class SageMakerStartPipelineExecution extends ScheduleTargetBase implemen
4546
super(props, pipeline.pipelineArn);
4647

4748
if (props.pipelineParameterList !== undefined && props.pipelineParameterList.length > 200) {
48-
throw new Error(`pipelineParameterList length must be between 0 and 200, got ${props.pipelineParameterList.length}`);
49+
throw new ValidationError(`pipelineParameterList length must be between 0 and 200, got ${props.pipelineParameterList.length}`, pipeline);
4950
}
5051
}
5152

packages/aws-cdk-lib/aws-scheduler-targets/lib/sqs-send-message.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
22
import { IRole } from '../../aws-iam';
33
import { ISchedule, IScheduleTarget, ScheduleTargetConfig } from '../../aws-scheduler';
44
import * as sqs from '../../aws-sqs';
5-
import { Token } from '../../core';
5+
import { Token, ValidationError } from '../../core';
66

77
/**
88
* Properties for a SQS Queue Target
@@ -35,16 +35,16 @@ export class SqsSendMessage extends ScheduleTargetBase implements IScheduleTarge
3535

3636
if (props.messageGroupId !== undefined) {
3737
if (!Token.isUnresolved(props.messageGroupId) && (props.messageGroupId.length < 1 || props.messageGroupId.length > 128)) {
38-
throw new Error(`messageGroupId length must be between 1 and 128, got ${props.messageGroupId.length}`);
38+
throw new ValidationError(`messageGroupId length must be between 1 and 128, got ${props.messageGroupId.length}`, queue);
3939
}
4040
if (!queue.fifo) {
41-
throw new Error('target must be a FIFO queue if messageGroupId is specified');
41+
throw new ValidationError('target must be a FIFO queue if messageGroupId is specified', queue);
4242
}
4343
if (!(queue.node.defaultChild as sqs.CfnQueue).contentBasedDeduplication) {
44-
throw new Error('contentBasedDeduplication must be true if the target is a FIFO queue');
44+
throw new ValidationError('contentBasedDeduplication must be true if the target is a FIFO queue', queue);
4545
}
4646
} else if (queue.fifo) {
47-
throw new Error('messageGroupId must be specified if the target is a FIFO queue');
47+
throw new ValidationError('messageGroupId must be specified if the target is a FIFO queue', queue);
4848
}
4949
}
5050

packages/aws-cdk-lib/aws-scheduler-targets/lib/target.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as iam from '../../aws-iam';
22
import { ISchedule, ScheduleTargetConfig, ScheduleTargetInput } from '../../aws-scheduler';
33
import { CfnSchedule } from '../../aws-scheduler/lib/scheduler.generated';
44
import * as sqs from '../../aws-sqs';
5-
import { Duration, PhysicalName, Stack, Token } from '../../core';
5+
import { Duration, PhysicalName, Stack, Token, UnscopedValidationError } from '../../core';
66
import { md5hash } from '../../core/lib/helpers-internal';
77

88
/**
@@ -157,10 +157,10 @@ export abstract class ScheduleTargetBase {
157157
if (maximumEventAge) {
158158
maxAge = maximumEventAge.toSeconds({ integral: true });
159159
if (maxAge > maxMaxAge) {
160-
throw new Error('Maximum event age is 1 day');
160+
throw new UnscopedValidationError('Maximum event age is 1 day');
161161
}
162162
if (maxAge < minMaxAge) {
163-
throw new Error('Minimum event age is 1 minute');
163+
throw new UnscopedValidationError('Minimum event age is 1 minute');
164164
}
165165
}
166166
let maxAttempts = 185;

packages/aws-cdk-lib/aws-scheduler-targets/lib/universal.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ScheduleTargetBase, ScheduleTargetBaseProps } from './target';
22
import { IRole, PolicyStatement } from '../../aws-iam';
33
import { IScheduleTarget } from '../../aws-scheduler';
4-
import { Annotations, Aws, Token } from '../../core';
4+
import { Annotations, Aws, Token, UnscopedValidationError } from '../../core';
55
import { awsSdkToIamAction } from '../../custom-resources/lib/helpers-internal';
66

77
/**
@@ -80,13 +80,13 @@ export class Universal extends ScheduleTargetBase implements IScheduleTarget {
8080
const action = props.action;
8181

8282
if (!Token.isUnresolved(service) && service !== service.toLowerCase()) {
83-
throw new Error(`API service must be lowercase, got: ${service}`);
83+
throw new UnscopedValidationError(`API service must be lowercase, got: ${service}`);
8484
}
8585
if (!Token.isUnresolved(action) && !action.startsWith(action[0]?.toLowerCase())) {
86-
throw new Error(`API action must be camelCase, got: ${action}`);
86+
throw new UnscopedValidationError(`API action must be camelCase, got: ${action}`);
8787
}
8888
if (!Token.isUnresolved(action) && NOT_SUPPORTED_ACTION_PREFIX.some(prefix => action.startsWith(prefix))) {
89-
throw new Error(`Read-only API action is not supported by EventBridge Scheduler: ${service}:${action}`);
89+
throw new UnscopedValidationError(`Read-only API action is not supported by EventBridge Scheduler: ${service}:${action}`);
9090
}
9191

9292
const arn = `arn:${Aws.PARTITION}:scheduler:::aws-sdk:${service}:${action}`;

packages/aws-cdk-lib/aws-scheduler/lib/schedule-expression.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as events from '../../aws-events';
2-
import { Duration, TimeZone } from '../../core';
2+
import { Duration, TimeZone, UnscopedValidationError } from '../../core';
33

44
/**
55
* ScheduleExpression for EventBridge Schedule
@@ -22,7 +22,7 @@ export abstract class ScheduleExpression {
2222
return new LiteralScheduleExpression(`at(${literal})`, timeZone ?? TimeZone.ETC_UTC);
2323
} catch (e) {
2424
if (e instanceof RangeError) {
25-
throw new Error('Invalid date');
25+
throw new UnscopedValidationError('Invalid date');
2626
}
2727
throw e;
2828
}

packages/aws-cdk-lib/aws-scheduler/lib/schedule.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CfnSchedule } from './scheduler.generated';
55
import { IScheduleTarget } from './target';
66
import * as cloudwatch from '../../aws-cloudwatch';
77
import * as kms from '../../aws-kms';
8-
import { Arn, ArnFormat, Duration, IResource, Resource, Token } from '../../core';
8+
import { Arn, ArnFormat, Duration, IResource, Resource, Token, UnscopedValidationError, ValidationError } from '../../core';
99
import { addConstructMetadata } from '../../core/lib/metadata-resource';
1010

1111
/**
@@ -46,7 +46,7 @@ export class TimeWindow {
4646
*/
4747
public static flexible(maxWindow: Duration): TimeWindow {
4848
if (maxWindow.toMinutes() < 1 || maxWindow.toMinutes() > 1440) {
49-
throw new Error(`The provided duration must be between 1 minute and 1440 minutes, got ${maxWindow.toMinutes()}`);
49+
throw new UnscopedValidationError(`The provided duration must be between 1 minute and 1440 minutes, got ${maxWindow.toMinutes()}`);
5050
}
5151
return new TimeWindow('FLEXIBLE', maxWindow);
5252
}
@@ -304,7 +304,7 @@ export class Schedule extends Resource implements ISchedule {
304304

305305
this.validateTimeFrame(props.start, props.end);
306306
if (props.scheduleName && !Token.isUnresolved(props.scheduleName) && props.scheduleName.length > 64) {
307-
throw new Error(`scheduleName cannot be longer than 64 characters, got: ${props.scheduleName.length}`);
307+
throw new ValidationError(`scheduleName cannot be longer than 64 characters, got: ${props.scheduleName.length}`, this);
308308
}
309309

310310
const resource = new CfnSchedule(this, 'Resource', {
@@ -349,10 +349,10 @@ export class Schedule extends Resource implements ISchedule {
349349
};
350350

351351
if (policy.maximumEventAgeInSeconds && (policy.maximumEventAgeInSeconds < 60 || policy.maximumEventAgeInSeconds > 86400)) {
352-
throw new Error(`maximumEventAgeInSeconds must be between 60 and 86400, got ${policy.maximumEventAgeInSeconds}`);
352+
throw new ValidationError(`maximumEventAgeInSeconds must be between 60 and 86400, got ${policy.maximumEventAgeInSeconds}`, this);
353353
}
354354
if (policy.maximumRetryAttempts && (policy.maximumRetryAttempts < 0 || policy.maximumRetryAttempts > 185)) {
355-
throw new Error(`maximumRetryAttempts must be between 0 and 185, got ${policy.maximumRetryAttempts}`);
355+
throw new ValidationError(`maximumRetryAttempts must be between 0 and 185, got ${policy.maximumRetryAttempts}`, this);
356356
}
357357

358358
const isEmptyPolicy = Object.values(policy).every(value => value === undefined);
@@ -361,7 +361,7 @@ export class Schedule extends Resource implements ISchedule {
361361

362362
private validateTimeFrame(start?: Date, end?: Date) {
363363
if (start && end && start >= end) {
364-
throw new Error(`start must precede end, got start: ${start.toISOString()}, end: ${end.toISOString()}`);
364+
throw new ValidationError(`start must precede end, got start: ${start.toISOString()}, end: ${end.toISOString()}`, this);
365365
}
366366
}
367367
}

0 commit comments

Comments
 (0)