Skip to content

Commit abd4a3e

Browse files
authored
feat(applicationautoscaling): throw ValidationError instead of untyped errors (#33172)
### Issue `aws-applicationautoscaling ` 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 04efe6c commit abd4a3e

File tree

6 files changed

+25
-19
lines changed

6 files changed

+25
-19
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const enableNoThrowDefaultErrorIn = [
2222
'aws-apigatewayv2',
2323
'aws-apigatewayv2-authorizers',
2424
'aws-apigatewayv2-integrations',
25+
'aws-applicationautoscaling',
2526
'aws-cognito',
2627
'aws-elasticloadbalancing',
2728
'aws-elasticloadbalancingv2',

packages/aws-cdk-lib/aws-applicationautoscaling/lib/scalable-target.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { BasicStepScalingPolicyProps, StepScalingPolicy } from './step-scaling-p
55
import { BasicTargetTrackingScalingPolicyProps, TargetTrackingScalingPolicy } from './target-tracking-scaling-policy';
66
import * as iam from '../../aws-iam';
77
import { IResource, Lazy, Resource, TimeZone, withResolved } from '../../core';
8+
import { ValidationError } from '../../core/lib/errors';
89

910
export interface IScalableTarget extends IResource {
1011
/**
@@ -100,19 +101,19 @@ export class ScalableTarget extends Resource implements IScalableTarget {
100101

101102
withResolved(props.maxCapacity, max => {
102103
if (max < 0) {
103-
throw new RangeError(`maxCapacity cannot be negative, got: ${props.maxCapacity}`);
104+
throw new ValidationError(`maxCapacity cannot be negative, got: ${props.maxCapacity}`, scope);
104105
}
105106
});
106107

107108
withResolved(props.minCapacity, min => {
108109
if (min < 0) {
109-
throw new RangeError(`minCapacity cannot be negative, got: ${props.minCapacity}`);
110+
throw new ValidationError(`minCapacity cannot be negative, got: ${props.minCapacity}`, scope);
110111
}
111112
});
112113

113114
withResolved(props.minCapacity, props.maxCapacity, (min, max) => {
114115
if (max < min) {
115-
throw new RangeError(`minCapacity (${props.minCapacity}) should be lower than maxCapacity (${props.maxCapacity})`);
116+
throw new ValidationError(`minCapacity (${props.minCapacity}) should be lower than maxCapacity (${props.maxCapacity})`, scope);
116117
}
117118
});
118119

@@ -145,7 +146,7 @@ export class ScalableTarget extends Resource implements IScalableTarget {
145146
*/
146147
public scaleOnSchedule(id: string, action: ScalingSchedule) {
147148
if (action.minCapacity === undefined && action.maxCapacity === undefined) {
148-
throw new Error(`You must supply at least one of minCapacity or maxCapacity, got ${JSON.stringify(action)}`);
149+
throw new ValidationError(`You must supply at least one of minCapacity or maxCapacity, got ${JSON.stringify(action)}`, this);
149150
}
150151

151152
// add a warning on synth when minute is not defined in a cron schedule

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Construct } from 'constructs';
22
import { Annotations, Duration } from '../../core';
3+
import { UnscopedValidationError } from '../../core/lib/errors';
34

45
/**
56
* Schedule for scheduled scaling actions
@@ -21,12 +22,12 @@ export abstract class Schedule {
2122
if (duration.isUnresolved()) {
2223
const validDurationUnit = ['minute', 'minutes', 'hour', 'hours', 'day', 'days'];
2324
if (!validDurationUnit.includes(duration.unitLabel())) {
24-
throw new Error("Allowed units for scheduling are: 'minute', 'minutes', 'hour', 'hours', 'day' or 'days'");
25+
throw new UnscopedValidationError("Allowed units for scheduling are: 'minute', 'minutes', 'hour', 'hours', 'day' or 'days'");
2526
}
2627
return new LiteralSchedule(`rate(${duration.formatTokenToNumber()})`);
2728
}
2829
if (duration.toSeconds() === 0) {
29-
throw new Error('Duration cannot be 0');
30+
throw new UnscopedValidationError('Duration cannot be 0');
3031
}
3132

3233
let rate = maybeRate(duration.toDays({ integral: false }), 'day');
@@ -47,7 +48,7 @@ export abstract class Schedule {
4748
*/
4849
public static cron(options: CronOptions): Schedule {
4950
if (options.weekDay !== undefined && options.day !== undefined) {
50-
throw new Error('Cannot supply both \'day\' and \'weekDay\', use at most one');
51+
throw new UnscopedValidationError('Cannot supply both \'day\' and \'weekDay\', use at most one');
5152
}
5253

5354
const minute = fallback(options.minute, '*');

packages/aws-cdk-lib/aws-applicationautoscaling/lib/step-scaling-action.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Construct } from 'constructs';
22
import { CfnScalingPolicy } from './applicationautoscaling.generated';
33
import { IScalableTarget } from './scalable-target';
44
import * as cdk from '../../core';
5+
import { ValidationError } from '../../core/lib/errors';
56

67
/**
78
* Properties for a scaling policy
@@ -102,7 +103,7 @@ export class StepScalingAction extends Construct {
102103
*/
103104
public addAdjustment(adjustment: AdjustmentTier) {
104105
if (adjustment.lowerBound === undefined && adjustment.upperBound === undefined) {
105-
throw new Error('At least one of lowerBound or upperBound is required');
106+
throw new ValidationError('At least one of lowerBound or upperBound is required', this);
106107
}
107108
this.adjustments.push({
108109
metricIntervalLowerBound: adjustment.lowerBound,

packages/aws-cdk-lib/aws-applicationautoscaling/lib/step-scaling-policy.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { AdjustmentType, MetricAggregationType, StepScalingAction } from './step
44
import { findAlarmThresholds, normalizeIntervals } from '../../aws-autoscaling-common';
55
import * as cloudwatch from '../../aws-cloudwatch';
66
import * as cdk from '../../core';
7+
import { ValidationError } from '../../core/lib/errors';
78

89
export interface BasicStepScalingPolicyProps {
910
/**
@@ -110,28 +111,28 @@ export class StepScalingPolicy extends Construct {
110111
super(scope, id);
111112

112113
if (props.scalingSteps.length < 2) {
113-
throw new Error('You must supply at least 2 intervals for autoscaling');
114+
throw new ValidationError('You must supply at least 2 intervals for autoscaling', scope);
114115
}
115116

116117
if (props.scalingSteps.length > 40) {
117-
throw new Error(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`);
118+
throw new ValidationError(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`, scope);
118119
}
119120

120121
if (props.evaluationPeriods !== undefined && !cdk.Token.isUnresolved(props.evaluationPeriods) && props.evaluationPeriods < 1) {
121-
throw new Error(`evaluationPeriods cannot be less than 1, got: ${props.evaluationPeriods}`);
122+
throw new ValidationError(`evaluationPeriods cannot be less than 1, got: ${props.evaluationPeriods}`, scope);
122123
}
123124
if (props.datapointsToAlarm !== undefined) {
124125
if (props.evaluationPeriods === undefined) {
125-
throw new Error('evaluationPeriods must be set if datapointsToAlarm is set');
126+
throw new ValidationError('evaluationPeriods must be set if datapointsToAlarm is set', scope);
126127
}
127128
if (!cdk.Token.isUnresolved(props.datapointsToAlarm) && props.datapointsToAlarm < 1) {
128-
throw new Error(`datapointsToAlarm cannot be less than 1, got: ${props.datapointsToAlarm}`);
129+
throw new ValidationError(`datapointsToAlarm cannot be less than 1, got: ${props.datapointsToAlarm}`, scope);
129130
}
130131
if (!cdk.Token.isUnresolved(props.datapointsToAlarm)
131132
&& !cdk.Token.isUnresolved(props.evaluationPeriods)
132133
&& props.evaluationPeriods < props.datapointsToAlarm
133134
) {
134-
throw new Error(`datapointsToAlarm must be less than or equal to evaluationPeriods, got datapointsToAlarm: ${props.datapointsToAlarm}, evaluationPeriods: ${props.evaluationPeriods}`);
135+
throw new ValidationError(`datapointsToAlarm must be less than or equal to evaluationPeriods, got datapointsToAlarm: ${props.datapointsToAlarm}, evaluationPeriods: ${props.evaluationPeriods}`, scope);
135136
}
136137
}
137138

packages/aws-cdk-lib/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { CfnScalingPolicy } from './applicationautoscaling.generated';
33
import { IScalableTarget } from './scalable-target';
44
import * as cloudwatch from '../../aws-cloudwatch';
55
import * as cdk from '../../core';
6+
import { ValidationError } from '../../core/lib/errors';
67

78
/**
89
* Base interface for target tracking props
@@ -123,11 +124,11 @@ export class TargetTrackingScalingPolicy extends Construct {
123124

124125
constructor(scope: Construct, id: string, props: TargetTrackingScalingPolicyProps) {
125126
if ((props.customMetric === undefined) === (props.predefinedMetric === undefined)) {
126-
throw new Error('Exactly one of \'customMetric\' or \'predefinedMetric\' must be specified.');
127+
throw new ValidationError('Exactly one of \'customMetric\' or \'predefinedMetric\' must be specified.', scope);
127128
}
128129

129130
if (props.customMetric && !props.customMetric.toMetricConfig().metricStat) {
130-
throw new Error('Only direct metrics are supported for Target Tracking. Use Step Scaling or supply a Metric object.');
131+
throw new ValidationError('Only direct metrics are supported for Target Tracking. Use Step Scaling or supply a Metric object.', scope);
131132
}
132133

133134
super(scope, id);
@@ -142,7 +143,7 @@ export class TargetTrackingScalingPolicy extends Construct {
142143
policyType: 'TargetTrackingScaling',
143144
scalingTargetId: props.scalingTarget.scalableTargetId,
144145
targetTrackingScalingPolicyConfiguration: {
145-
customizedMetricSpecification: renderCustomMetric(props.customMetric),
146+
customizedMetricSpecification: renderCustomMetric(this, props.customMetric),
146147
disableScaleIn: props.disableScaleIn,
147148
predefinedMetricSpecification: predefinedMetric !== undefined ? {
148149
predefinedMetricType: predefinedMetric,
@@ -158,12 +159,12 @@ export class TargetTrackingScalingPolicy extends Construct {
158159
}
159160
}
160161

161-
function renderCustomMetric(metric?: cloudwatch.IMetric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined {
162+
function renderCustomMetric(scope: Construct, metric?: cloudwatch.IMetric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined {
162163
if (!metric) { return undefined; }
163164
const c = metric.toMetricConfig().metricStat!;
164165

165166
if (c.statistic.startsWith('p')) {
166-
throw new Error(`Cannot use statistic '${c.statistic}' for Target Tracking: only 'Average', 'Minimum', 'Maximum', 'SampleCount', and 'Sum' are supported.`);
167+
throw new ValidationError(`Cannot use statistic '${c.statistic}' for Target Tracking: only 'Average', 'Minimum', 'Maximum', 'SampleCount', and 'Sum' are supported.`, scope);
167168
}
168169

169170
return {

0 commit comments

Comments
 (0)