Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/aws-cdk-lib/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const enableNoThrowDefaultErrorIn = [
'aws-elasticloadbalancingv2',
'aws-elasticloadbalancingv2-actions',
'aws-elasticloadbalancingv2-targets',
'aws-events',
'aws-fsx',
'aws-kinesis',
'aws-kinesisfirehose',
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk-lib/aws-events/lib/api-destination.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Construct } from 'constructs';
import { HttpMethod, IConnection } from './connection';
import { CfnApiDestination } from './events.generated';
import { ArnFormat, IResource, Resource, Stack } from '../../core';
import { ArnFormat, IResource, Resource, Stack, UnscopedValidationError } from '../../core';
import { addConstructMetadata } from '../../core/lib/metadata-resource';

/**
Expand Down Expand Up @@ -100,7 +100,7 @@ export class ApiDestination extends Resource implements IApiDestination {
).resourceName;

if (!apiDestinationName) {
throw new Error(`Could not extract Api Destionation name from ARN: '${attrs.apiDestinationArn}'`);
throw new UnscopedValidationError(`Could not extract Api Destionation name from ARN: '${attrs.apiDestinationArn}'`);
}

class Import extends Resource implements ApiDestination {
Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk-lib/aws-events/lib/connection.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Construct } from 'constructs';
import { CfnConnection } from './events.generated';
import { IResource, Resource, Stack, SecretValue } from '../../core';
import { IResource, Resource, Stack, SecretValue, UnscopedValidationError } from '../../core';
import { addConstructMetadata } from '../../core/lib/metadata-resource';

/**
Expand Down Expand Up @@ -100,7 +100,7 @@ export abstract class Authorization {
*/
public static oauth(props: OAuthAuthorizationProps): Authorization {
if (![HttpMethod.POST, HttpMethod.GET, HttpMethod.PUT].includes(props.httpMethod)) {
throw new Error('httpMethod must be one of GET, POST, PUT');
throw new UnscopedValidationError('httpMethod must be one of GET, POST, PUT');
}

return new class extends Authorization {
Expand Down
18 changes: 9 additions & 9 deletions packages/aws-cdk-lib/aws-events/lib/event-bus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CfnEventBus, CfnEventBusPolicy } from './events.generated';
import * as iam from '../../aws-iam';
import * as kms from '../../aws-kms';
import * as sqs from '../../aws-sqs';
import { Annotations, ArnFormat, FeatureFlags, IResource, Lazy, Names, Resource, Stack, Token } from '../../core';
import { Annotations, ArnFormat, FeatureFlags, IResource, Lazy, Names, Resource, Stack, Token, UnscopedValidationError, ValidationError } from '../../core';
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
import * as cxapi from '../../cx-api';

Expand Down Expand Up @@ -311,23 +311,23 @@ export class EventBus extends EventBusBase {
const eventBusNameRegex = /^[\/\.\-_A-Za-z0-9]{1,256}$/;

if (eventBusName !== undefined && eventSourceName !== undefined) {
throw new Error(
throw new UnscopedValidationError(
'\'eventBusName\' and \'eventSourceName\' cannot both be provided',
);
}

if (eventBusName !== undefined) {
if (!Token.isUnresolved(eventBusName)) {
if (eventBusName === 'default') {
throw new Error(
throw new UnscopedValidationError(
'\'eventBusName\' must not be \'default\'',
);
} else if (eventBusName.indexOf('/') > -1) {
throw new Error(
throw new UnscopedValidationError(
'\'eventBusName\' must not contain \'/\'',
);
} else if (!eventBusNameRegex.test(eventBusName)) {
throw new Error(
throw new UnscopedValidationError(
`'eventBusName' must satisfy: ${eventBusNameRegex}`,
);
}
Expand All @@ -340,11 +340,11 @@ export class EventBus extends EventBusBase {
// Ex: aws.partner/PartnerName/acct1/repo1
const eventSourceNameRegex = /^aws\.partner(\/[\.\-_A-Za-z0-9]+){2,}$/;
if (!eventSourceNameRegex.test(eventSourceName)) {
throw new Error(
throw new UnscopedValidationError(
`'eventSourceName' must satisfy: ${eventSourceNameRegex}`,
);
} else if (!eventBusNameRegex.test(eventSourceName)) {
throw new Error(
throw new UnscopedValidationError(
`'eventSourceName' must satisfy: ${eventBusNameRegex}`,
);
}
Expand Down Expand Up @@ -387,7 +387,7 @@ export class EventBus extends EventBusBase {
addConstructMetadata(this, props);

if (props?.description && !Token.isUnresolved(props.description) && props.description.length > 512) {
throw new Error(`description must be less than or equal to 512 characters, got ${props.description.length}`);
throw new ValidationError(`description must be less than or equal to 512 characters, got ${props.description.length}`, this);
}

const eventBus = new CfnEventBus(this, 'Resource', {
Expand Down Expand Up @@ -446,7 +446,7 @@ export class EventBus extends EventBusBase {
public addToResourcePolicy(statement: iam.PolicyStatement): iam.AddToResourcePolicyResult {
// If no sid is provided, generate one based on the event bus id
if (statement.sid == null) {
throw new Error('Event Bus policy statements must have a sid');
throw new ValidationError('Event Bus policy statements must have a sid', this);
}

// In order to generate new statementIDs for the change in https://github.com/aws/aws-cdk/pull/27340
Expand Down
18 changes: 9 additions & 9 deletions packages/aws-cdk-lib/aws-events/lib/event-pattern.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { captureStackTrace, IResolvable, IResolveContext, Token } from '../../core';
import { captureStackTrace, IResolvable, IResolveContext, Token, UnscopedValidationError } from '../../core';

type ComparisonOperator = '>' | '>=' | '<' | '<=' | '=';

Expand Down Expand Up @@ -94,7 +94,7 @@ export class Match implements IResolvable {
const ipv6Regex = /^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/igm;

if (!ipv4Regex.test(range) && !ipv6Regex.test(range)) {
throw new Error(`Invalid IP address range: ${range}`);
throw new UnscopedValidationError(`Invalid IP address range: ${range}`);
}

return this.fromObjects([{ cidr: range }]);
Expand All @@ -114,14 +114,14 @@ export class Match implements IResolvable {
*/
public static anythingBut(...values: any[]): string[] {
if (values.length === 0) {
throw new Error('anythingBut matchers must be non-empty lists');
throw new UnscopedValidationError('anythingBut matchers must be non-empty lists');
}

const allNumbers = values.every(v => typeof (v) === 'number');
const allStrings = values.every(v => typeof (v) === 'string');

if (!(allNumbers || allStrings)) {
throw new Error('anythingBut matchers must be lists that contain only strings or only numbers.');
throw new UnscopedValidationError('anythingBut matchers must be lists that contain only strings or only numbers.');
}

return this.fromObjects([{ 'anything-but': values }]);
Expand Down Expand Up @@ -200,7 +200,7 @@ export class Match implements IResolvable {
*/
public static interval(lower: number, upper: number): string[] {
if (lower > upper) {
throw new Error(`Invalid interval: [${lower}, ${upper}]`);
throw new UnscopedValidationError(`Invalid interval: [${lower}, ${upper}]`);
}

return Match.allOf(Match.greaterThanOrEqual(lower), Match.lessThanOrEqual(upper));
Expand All @@ -211,7 +211,7 @@ export class Match implements IResolvable {
*/
public static allOf(...matchers: any[]): string[] {
if (matchers.length === 0) {
throw new Error('A list of matchers must contain at least one element.');
throw new UnscopedValidationError('A list of matchers must contain at least one element.');
}

return this.fromMergedObjects(matchers);
Expand All @@ -222,14 +222,14 @@ export class Match implements IResolvable {
*/
public static anyOf(...matchers: any[]): string[] {
if (matchers.length === 0) {
throw new Error('A list of matchers must contain at least one element.');
throw new UnscopedValidationError('A list of matchers must contain at least one element.');
}
return this.fromObjects(matchers);
}

private static anythingButConjunction(filterKey: string, values: string[]): string[] {
if (values.length === 0) {
throw new Error('anythingBut matchers must be non-empty lists');
throw new UnscopedValidationError('anythingBut matchers must be non-empty lists');
}

// When there is a single value return it, otherwise return the array
Expand Down Expand Up @@ -266,7 +266,7 @@ export class Match implements IResolvable {
// This is the only supported case for merging at the moment.
// We can generalize this logic if EventBridge starts supporting more cases in the future.
if (!matchers.every(matcher => matcher?.numeric)) {
throw new Error('Only numeric matchers can be merged into a single matcher.');
throw new UnscopedValidationError('Only numeric matchers can be merged into a single matcher.');
}

return [{ numeric: matchers.flatMap(matcher => matcher.numeric) }];
Expand Down
3 changes: 2 additions & 1 deletion packages/aws-cdk-lib/aws-events/lib/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IRule } from './rule-ref';
import {
captureStackTrace, DefaultTokenResolver, IResolvable,
IResolveContext, Lazy, Stack, StringConcat, Token, Tokenization,
UnscopedValidationError,
} from '../../core';

/**
Expand Down Expand Up @@ -169,7 +170,7 @@ export class FieldAwareEventInput extends RuleTargetInput {

const key = keyForField(t);
if (inputPathsMap[key] && inputPathsMap[key] !== t.path) {
throw new Error(`Single key '${key}' is used for two different JSON paths: '${t.path}' and '${inputPathsMap[key]}'`);
throw new UnscopedValidationError(`Single key '${key}' is used for two different JSON paths: '${t.path}' and '${inputPathsMap[key]}'`);
}
inputPathsMap[key] = t.path;

Expand Down
18 changes: 9 additions & 9 deletions packages/aws-cdk-lib/aws-events/lib/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Schedule } from './schedule';
import { IRuleTarget } from './target';
import { mergeEventPattern, renderEventPattern } from './util';
import { IRole, PolicyStatement, Role, ServicePrincipal } from '../../aws-iam';
import { App, IResource, Lazy, Names, Resource, Stack, Token, TokenComparison, PhysicalName, ArnFormat, Annotations } from '../../core';
import { App, IResource, Lazy, Names, Resource, Stack, Token, TokenComparison, PhysicalName, ArnFormat, Annotations, ValidationError } from '../../core';
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';

/**
Expand Down Expand Up @@ -107,7 +107,7 @@ export class Rule extends Resource implements IRule {
addConstructMetadata(this, props);

if (props.eventBus && props.schedule) {
throw new Error('Cannot associate rule with \'eventBus\' when using \'schedule\'');
throw new ValidationError('Cannot associate rule with \'eventBus\' when using \'schedule\'', this);
}

this.description = props.description;
Expand Down Expand Up @@ -184,27 +184,27 @@ export class Rule extends Resource implements IRule {

// for cross-account or cross-region events, we require a concrete target account and region
if (!targetAccount || Token.isUnresolved(targetAccount)) {
throw new Error('You need to provide a concrete account for the target stack when using cross-account or cross-region events');
throw new ValidationError('You need to provide a concrete account for the target stack when using cross-account or cross-region events', this);
}
if (!targetRegion || Token.isUnresolved(targetRegion)) {
throw new Error('You need to provide a concrete region for the target stack when using cross-account or cross-region events');
throw new ValidationError('You need to provide a concrete region for the target stack when using cross-account or cross-region events', this);
}
if (Token.isUnresolved(sourceAccount)) {
throw new Error('You need to provide a concrete account for the source stack when using cross-account or cross-region events');
throw new ValidationError('You need to provide a concrete account for the source stack when using cross-account or cross-region events', this);
}

// Don't exactly understand why this code was here (seems unlikely this rule would be violated), but
// let's leave it in nonetheless.
const sourceApp = this.node.root;
if (!sourceApp || !App.isApp(sourceApp)) {
throw new Error('Event stack which uses cross-account or cross-region targets must be part of a CDK app');
throw new ValidationError('Event stack which uses cross-account or cross-region targets must be part of a CDK app', this);
}
const targetApp = Node.of(targetProps.targetResource).root;
if (!targetApp || !App.isApp(targetApp)) {
throw new Error('Target stack which uses cross-account or cross-region event targets must be part of a CDK app');
throw new ValidationError('Target stack which uses cross-account or cross-region event targets must be part of a CDK app', this);
}
if (sourceApp !== targetApp) {
throw new Error('Event stack and target stack must belong to the same CDK app');
throw new ValidationError('Event stack and target stack must belong to the same CDK app', this);
}

// The target of this Rule will be the default event bus of the target environment
Expand Down Expand Up @@ -432,7 +432,7 @@ export class Rule extends Resource implements IRule {
}

// For now, we don't do the work for the support stack yet
throw new Error('Cannot create a cross-account or cross-region rule for an imported resource (create a stack with the right environment for the imported resource)');
throw new ValidationError('Cannot create a cross-account or cross-region rule for an imported resource (create a stack with the right environment for the imported resource)', this);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/aws-cdk-lib/aws-events/lib/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Construct } from 'constructs';
import { Annotations, Duration } from '../../core';
import { Annotations, Duration, UnscopedValidationError } from '../../core';

/**
* Schedule for scheduled event rules
Expand Down Expand Up @@ -27,12 +27,12 @@ export abstract class Schedule {
if (duration.isUnresolved()) {
const validDurationUnit = ['minute', 'minutes', 'hour', 'hours', 'day', 'days'];
if (validDurationUnit.indexOf(duration.unitLabel()) === -1) {
throw new Error("Allowed units for scheduling are: 'minute', 'minutes', 'hour', 'hours', 'day', 'days'");
throw new UnscopedValidationError("Allowed units for scheduling are: 'minute', 'minutes', 'hour', 'hours', 'day', 'days'");
}
return new LiteralSchedule(`rate(${duration.formatTokenToNumber()})`);
}
if (duration.toMinutes() === 0) {
throw new Error('Duration cannot be 0');
throw new UnscopedValidationError('Duration cannot be 0');
}

let rate = maybeRate(duration.toDays({ integral: false }), 'day');
Expand All @@ -46,7 +46,7 @@ export abstract class Schedule {
*/
public static cron(options: CronOptions): Schedule {
if (options.weekDay !== undefined && options.day !== undefined) {
throw new Error('Cannot supply both \'day\' and \'weekDay\', use at most one');
throw new UnscopedValidationError('Cannot supply both \'day\' and \'weekDay\', use at most one');
}

const minute = fallback(options.minute, '*');
Expand Down
7 changes: 4 additions & 3 deletions packages/aws-cdk-lib/aws-events/lib/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EventPattern } from './event-pattern';
import { UnscopedValidationError } from '../../core';

/**
* Merge the `src` event pattern into the `dest` event pattern by adding all
Expand All @@ -15,7 +16,7 @@ export function mergeEventPattern(dest: any, src: any) {

function mergeObject(destObj: any, srcObj: any) {
if (typeof(srcObj) !== 'object') {
throw new Error(`Invalid event pattern '${JSON.stringify(srcObj)}', expecting an object or an array`);
throw new UnscopedValidationError(`Invalid event pattern '${JSON.stringify(srcObj)}', expecting an object or an array`);
}

for (const field of Object.keys(srcObj)) {
Expand All @@ -25,7 +26,7 @@ export function mergeEventPattern(dest: any, src: any) {
if (srcValue === undefined) { continue; }

if (typeof(srcValue) !== 'object') {
throw new Error(`Invalid event pattern field { ${field}: ${JSON.stringify(srcValue)} }. All fields must be arrays`);
throw new UnscopedValidationError(`Invalid event pattern field { ${field}: ${JSON.stringify(srcValue)} }. All fields must be arrays`);
}

// dest doesn't have this field
Expand All @@ -35,7 +36,7 @@ export function mergeEventPattern(dest: any, src: any) {
}

if (Array.isArray(srcValue) !== Array.isArray(destValue)) {
throw new Error(`Invalid event pattern field ${field}. ` +
throw new UnscopedValidationError(`Invalid event pattern field ${field}. ` +
`Type mismatch between existing pattern ${JSON.stringify(destValue)} and added pattern ${JSON.stringify(srcValue)}`);
}

Expand Down
Loading