Skip to content

Commit 4818900

Browse files
authored
2 parents 0ad4f3b + 9e6d78b commit 4818900

28 files changed

+435
-170
lines changed

.github/workflows/enum-auto-updater.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- name: Check for changes
3434
id: static-mapping-check
3535
run: |
36+
cd tools/@aws-cdk/enum-updater
3637
if [[ -n "$(git status --porcelain ./lib/static-enum-mapping.json)" ]]; then
3738
echo "changes=true" >> $GITHUB_OUTPUT
3839
else
@@ -42,6 +43,7 @@ jobs:
4243
- name: Create PR for static mapping changes
4344
if: steps.static-mapping-check.outputs.changes == 'true'
4445
run: |
46+
cd tools/@aws-cdk/enum-updater
4547
git config --global user.name 'aws-cdk-automation'
4648
git config --global user.email '[email protected]'
4749
@@ -56,8 +58,10 @@ jobs:
5658
gh pr create --title "chore: update enum static mapping" \
5759
--body "This PR updates the CDK enum mapping file." \
5860
--base main \
59-
--head "$branchName"
61+
--head "$branchName" \
6062
--label "contribution/core,pr-linter/exempt-integ-test,pr-linter/exempt-readme,pr-linter/exempt-test"
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6165

6266
- name: Identify Missing Values and Apply Code Changes
6367
run: |
@@ -77,6 +81,8 @@ jobs:
7781
if: steps.git-check.outputs.changes == 'true'
7882
run: |
7983
# Iterate through each module directory that has changes
84+
git config --global user.name 'aws-cdk-automation'
85+
git config --global user.email '[email protected]'
8086
for module in $(git diff --name-only | grep -E '^packages/(@aws-cdk|aws-cdk-lib)/.*' | sed -E 's|^packages/(@aws-cdk\|aws-cdk-lib)/([^/]+).*|\2|' | sort -u); do
8187
moduleName=$(basename $module)
8288
@@ -112,7 +118,7 @@ jobs:
112118
gh pr create --title "chore(${moduleName#aws-}): add new enum values for ${moduleName#aws-}" \
113119
--body "This PR updates the enum values for ${moduleName#aws-}." \
114120
--base main \
115-
--head "$branchName"
121+
--head "$branchName" \
116122
--label "contribution/core,pr-linter/exempt-integ-test,pr-linter/exempt-readme,pr-linter/exempt-test"
117123
done
118124
env:

.github/workflows/github-merit-badger.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ jobs:
1717
badges: '[beginning-contributor,repeat-contributor,valued-contributor,admired-contributor,star-contributor,distinguished-contributor]'
1818
thresholds: '[0,3,6,13,25,50]'
1919
badge-type: 'achievement'
20-
ignore-usernames: '[rix0rrr,iliapolo,otaviomacedo,kaizencc,TheRealAmazonKendra,mrgrain,pahud,kellertk,ashishdhingra,khushail,moelasmar,paulhcsun,GavinZZ,xazhao,gracelu0,shikha372,godwingrs22,bergjaak,IanKonlog,Leo10Gama,samson-keung,scorbiere,jiayiwang7,saiyush,5d,iankhou,QuantumNeuralCoder,SimonCMoore,aws-cdk-automation,dependabot[bot],mergify[bot]]'
20+
ignore-usernames: '[rix0rrr,iliapolo,otaviomacedo,kaizencc,TheRealAmazonKendra,mrgrain,pahud,kellertk,ashishdhingra,khushail,moelasmar,paulhcsun,GavinZZ,xazhao,gracelu0,shikha372,godwingrs22,bergjaak,IanKonlog,Leo10Gama,samson-keung,scorbiere,jiayiwang7,saiyush,5d,iankhou,QuantumNeuralCoder,SimonCMoore,Y-JayKim,aws-cdk-automation,dependabot[bot],mergify[bot]]'

.mergify.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ pull_request_rules:
6363
- name: label core
6464
actions:
6565
label:
66-
add: [ contribution/core ]
66+
add: [contribution/core]
6767
conditions:
68-
- author~=^(rix0rrr|iliapolo|otaviomacedo|kaizencc|TheRealAmazonKendra|mrgrain|pahud|ashishdhingra|kellertk|moelasmar|paulhcsun|GavinZZ|xazhao|gracelu0|shikha372|QuantumNeuralCoder|godwingrs22|bergjaak|samson-keung|IanKonlog|Leo10Gama|scorbiere|jiayiwang7|saiyush|5d|iankhou|SimonCMoore)$
68+
- author~=^(rix0rrr|iliapolo|otaviomacedo|kaizencc|TheRealAmazonKendra|mrgrain|pahud|ashishdhingra|kellertk|moelasmar|paulhcsun|GavinZZ|xazhao|gracelu0|shikha372|QuantumNeuralCoder|godwingrs22|bergjaak|samson-keung|IanKonlog|Leo10Gama|scorbiere|jiayiwang7|saiyush|5d|iankhou|SimonCMoore|Y-JayKim)$
6969
- -label~="contribution/core"
7070
- name: automatic merge
7171
actions:
@@ -190,3 +190,14 @@ pull_request_rules:
190190
- "#changes-requested-reviews-by=0"
191191
- status-success~=AWS CodeBuild us-east-1
192192
- status-success=validate-pr
193+
priority_rules:
194+
- name: priority for queue `default-merge`
195+
conditions:
196+
- -label~=(blocked|do-not-merge)
197+
- label~=no-squash
198+
priority: 2500
199+
- name: priority for queue `priority-squash`
200+
conditions:
201+
- -label~=(blocked|do-not-merge|no-squash)
202+
- label~=priority-pr
203+
priority: 2250

CHANGELOG.v2.alpha.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [2.191.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.190.0-alpha.0...v2.191.0-alpha.0) (2025-04-22)
6+
7+
8+
### Features
9+
10+
* **location:** throw ValidationError instead of untyped errors ([#34174](https://github.com/aws/aws-cdk/issues/34174)) ([2ecf14a](https://github.com/aws/aws-cdk/commit/2ecf14a0c3e5a988532975536980d81589ea448e))
11+
* **msk:** throw ValidationError instead of untyped errors ([#34214](https://github.com/aws/aws-cdk/issues/34214)) ([02cb5a4](https://github.com/aws/aws-cdk/commit/02cb5a4284e9aad2f8cc4fc8fcb2c1aebe8f92be))
12+
513
## [2.190.0-alpha.0](https://github.com/aws/aws-cdk/compare/v2.189.1-alpha.0...v2.190.0-alpha.0) (2025-04-16)
614

715

CHANGELOG.v2.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
## [2.191.0](https://github.com/aws/aws-cdk/compare/v2.190.0...v2.191.0) (2025-04-22)
6+
7+
8+
### ⚠ BREAKING CHANGES TO EXPERIMENTAL FEATURES
9+
10+
* **cloudformation:** Some L1 resources experienced breaking changes due to
11+
updated CloudFormation resources. Please check the notes for each
12+
specific module for more information.
13+
* **neptune-alpha**: `Id` attribute is being removed from the
14+
AWS::Neptune::DBClusterParameterGroup and AWS::Neptune::DBParameterGroup
15+
resources
16+
* **aws-launchwizard**: `specifications` prop moved from required to
17+
optional in CfnDeployment
18+
* **aws-ses**: `attribute` attribute moved from required to optional in
19+
RuleBooleanToEvaluateProperty
20+
21+
### Features
22+
23+
* **cloudformation:** update L1 CloudFormation resource definitions ([#34207](https://github.com/aws/aws-cdk/issues/34207)) ([adfa416](https://github.com/aws/aws-cdk/commit/adfa416fba36a5cf25a6dcb1dbdbec18029d9860))
24+
* update L1 CloudFormation resource definitions ([e3483c2](https://github.com/aws/aws-cdk/commit/e3483c2dc947d41059b127db77a6382ad1ed6f62))
25+
* **codebuild:** add additional build images for lambda ([#34197](https://github.com/aws/aws-cdk/issues/34197)) ([5a265d1](https://github.com/aws/aws-cdk/commit/5a265d1f6f3ebfcade0cf2807b720a35845a0d60))
26+
* **rds:** add Aurora MySQL versions 2.12.4, 3.08.2 ([#34045](https://github.com/aws/aws-cdk/issues/34045)) ([1dd993e](https://github.com/aws/aws-cdk/commit/1dd993e51dc90b13cec18809173275944075afdb))
27+
528
## [2.190.0](https://github.com/aws/aws-cdk/compare/v2.189.1...v2.190.0) (2025-04-16)
629

730

packages/@aws-cdk/aws-location-alpha/.eslintrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,12 @@ baseConfig.parserOptions.project = __dirname + '/tsconfig.json';
44
baseConfig.rules['import/no-extraneous-dependencies'] = ['error', { devDependencies: true, peerDependencies: true } ];
55
baseConfig.rules['import/order'] = 'off';
66
baseConfig.rules['@aws-cdk/invalid-cfn-imports'] = 'off';
7+
baseConfig.rules['@cdklabs/no-throw-default-error'] = ['error'];
8+
baseConfig.overrides.push({
9+
files: ["./test/**"],
10+
rules: {
11+
"@cdklabs/no-throw-default-error": "off",
12+
},
13+
});
714

815
module.exports = baseConfig;

packages/@aws-cdk/aws-location-alpha/lib/geofence-collection.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as iam from 'aws-cdk-lib/aws-iam';
22
import * as kms from 'aws-cdk-lib/aws-kms';
3-
import { ArnFormat, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core';
3+
import { ArnFormat, IResource, Lazy, Resource, Stack, Token, UnscopedValidationError, ValidationError } from 'aws-cdk-lib/core';
44
import { Construct } from 'constructs';
55
import { CfnGeofenceCollection } from 'aws-cdk-lib/aws-location';
66
import { generateUniqueId } from './util';
@@ -81,7 +81,7 @@ export class GeofenceCollection extends Resource implements IGeofenceCollection
8181
const parsedArn = Stack.of(scope).splitArn(geofenceCollectionArn, ArnFormat.SLASH_RESOURCE_NAME);
8282

8383
if (!parsedArn.resourceName) {
84-
throw new Error(`Geofence Collection Arn ${geofenceCollectionArn} does not have a resource name.`);
84+
throw new UnscopedValidationError(`Geofence Collection Arn ${geofenceCollectionArn} does not have a resource name.`);
8585
}
8686

8787
class Import extends Resource implements IGeofenceCollection {
@@ -114,26 +114,25 @@ export class GeofenceCollection extends Resource implements IGeofenceCollection
114114
public readonly geofenceCollectionUpdateTime: string;
115115

116116
constructor(scope: Construct, id: string, props: GeofenceCollectionProps = {}) {
117+
super(scope, id, {
118+
physicalName: props.geofenceCollectionName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
119+
});
120+
// Enhanced CDK Analytics Telemetry
121+
addConstructMetadata(this, props);
122+
117123
if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) {
118-
throw new Error(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`);
124+
throw new ValidationError(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`, this);
119125
}
120126

121127
if (props.geofenceCollectionName !== undefined && !Token.isUnresolved(props.geofenceCollectionName)) {
122128
if (props.geofenceCollectionName.length < 1 || props.geofenceCollectionName.length > 100) {
123-
throw new Error(`\`geofenceCollectionName\` must be between 1 and 100 characters, got: ${props.geofenceCollectionName.length} characters.`);
129+
throw new ValidationError(`\`geofenceCollectionName\` must be between 1 and 100 characters, got: ${props.geofenceCollectionName.length} characters.`, this);
124130
}
125131

126132
if (!/^[-._\w]+$/.test(props.geofenceCollectionName)) {
127-
throw new Error(`\`geofenceCollectionName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.geofenceCollectionName}.`);
133+
throw new ValidationError(`\`geofenceCollectionName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.geofenceCollectionName}.`, this);
128134
}
129135
}
130-
131-
super(scope, id, {
132-
physicalName: props.geofenceCollectionName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
133-
});
134-
// Enhanced CDK Analytics Telemetry
135-
addConstructMetadata(this, props);
136-
137136
const geofenceCollection = new CfnGeofenceCollection(this, 'Resource', {
138137
collectionName: this.physicalName,
139138
description: props.description,

packages/@aws-cdk/aws-location-alpha/lib/map.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as iam from 'aws-cdk-lib/aws-iam';
2-
import { ArnFormat, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core';
2+
import { ArnFormat, IResource, Lazy, Resource, Stack, Token, UnscopedValidationError, ValidationError } from 'aws-cdk-lib/core';
33
import { Construct } from 'constructs';
44
import { CfnMap } from 'aws-cdk-lib/aws-location';
55
import { generateUniqueId } from './util';
@@ -241,7 +241,7 @@ export class Map extends Resource implements IMap {
241241
const parsedArn = Stack.of(scope).splitArn(mapArn, ArnFormat.SLASH_RESOURCE_NAME);
242242

243243
if (!parsedArn.resourceName) {
244-
throw new Error(`Map Arn ${mapArn} does not have a resource name.`);
244+
throw new UnscopedValidationError(`Map Arn ${mapArn} does not have a resource name.`);
245245
}
246246

247247
class Import extends Resource implements IMap {
@@ -274,26 +274,26 @@ export class Map extends Resource implements IMap {
274274
public readonly mapUpdateTime: string;
275275

276276
constructor(scope: Construct, id: string, props: MapProps) {
277+
super(scope, id, {
278+
physicalName: props.mapName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
279+
});
280+
// Enhanced CDK Analytics Telemetry
281+
addConstructMetadata(this, props);
282+
277283
if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) {
278-
throw new Error(`\`description\` must be between 0 and 1000 characters, got: ${props.description.length} characters.`);
284+
throw new ValidationError(`\`description\` must be between 0 and 1000 characters, got: ${props.description.length} characters.`, this);
279285
}
280286

281287
if (props.mapName !== undefined && !Token.isUnresolved(props.mapName)) {
282288
if (props.mapName.length < 1 || props.mapName.length > 100) {
283-
throw new Error(`\`mapName\` must be between 1 and 100 characters, got: ${props.mapName.length} characters.`);
289+
throw new ValidationError(`\`mapName\` must be between 1 and 100 characters, got: ${props.mapName.length} characters.`, this);
284290
}
285291

286292
if (!/^[-._\w]+$/.test(props.mapName)) {
287-
throw new Error(`\`mapName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.mapName}.`);
293+
throw new ValidationError(`\`mapName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.mapName}.`, this);
288294
}
289295
}
290296

291-
super(scope, id, {
292-
physicalName: props.mapName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
293-
});
294-
// Enhanced CDK Analytics Telemetry
295-
addConstructMetadata(this, props);
296-
297297
const map = new CfnMap(this, 'Resource', {
298298
configuration: {
299299
style: props.style,

packages/@aws-cdk/aws-location-alpha/lib/place-index.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as iam from 'aws-cdk-lib/aws-iam';
2-
import { ArnFormat, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core';
2+
import { ArnFormat, IResource, Lazy, Resource, Stack, Token, UnscopedValidationError, ValidationError } from 'aws-cdk-lib/core';
33
import { Construct } from 'constructs';
44
import { CfnPlaceIndex } from 'aws-cdk-lib/aws-location';
55
import { DataSource, generateUniqueId } from './util';
@@ -101,7 +101,7 @@ export class PlaceIndex extends Resource implements IPlaceIndex {
101101
const parsedArn = Stack.of(scope).splitArn(placeIndexArn, ArnFormat.SLASH_RESOURCE_NAME);
102102

103103
if (!parsedArn.resourceName) {
104-
throw new Error(`Place Index Arn ${placeIndexArn} does not have a resource name.`);
104+
throw new UnscopedValidationError(`Place Index Arn ${placeIndexArn} does not have a resource name.`);
105105
}
106106

107107
class Import extends Resource implements IPlaceIndex {
@@ -134,26 +134,26 @@ export class PlaceIndex extends Resource implements IPlaceIndex {
134134
public readonly placeIndexUpdateTime: string;
135135

136136
constructor(scope: Construct, id: string, props: PlaceIndexProps = {}) {
137+
super(scope, id, {
138+
physicalName: props.placeIndexName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
139+
});
140+
// Enhanced CDK Analytics Telemetry
141+
addConstructMetadata(this, props);
142+
137143
if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) {
138-
throw new Error(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`);
144+
throw new ValidationError(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`, this);
139145
}
140146

141147
if (props.placeIndexName !== undefined && !Token.isUnresolved(props.placeIndexName)) {
142148
if (props.placeIndexName.length < 1 || props.placeIndexName.length > 100) {
143-
throw new Error(`\`placeIndexName\` must be between 1 and 100 characters, got: ${props.placeIndexName.length} characters.`);
149+
throw new ValidationError(`\`placeIndexName\` must be between 1 and 100 characters, got: ${props.placeIndexName.length} characters.`, this);
144150
}
145151

146152
if (!/^[-._\w]+$/.test(props.placeIndexName)) {
147-
throw new Error(`\`placeIndexName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.placeIndexName}.`);
153+
throw new ValidationError(`\`placeIndexName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.placeIndexName}.`, this);
148154
}
149155
}
150156

151-
super(scope, id, {
152-
physicalName: props.placeIndexName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
153-
});
154-
// Enhanced CDK Analytics Telemetry
155-
addConstructMetadata(this, props);
156-
157157
const placeIndex = new CfnPlaceIndex(this, 'Resource', {
158158
indexName: this.physicalName,
159159
dataSource: props.dataSource ?? DataSource.ESRI,

packages/@aws-cdk/aws-location-alpha/lib/route-calculator.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as iam from 'aws-cdk-lib/aws-iam';
2-
import { ArnFormat, IResource, Lazy, Resource, Stack, Token } from 'aws-cdk-lib/core';
2+
import { ArnFormat, IResource, Lazy, Resource, Stack, Token, UnscopedValidationError, ValidationError } from 'aws-cdk-lib/core';
33
import { Construct } from 'constructs';
44
import { CfnRouteCalculator } from 'aws-cdk-lib/aws-location';
55
import { generateUniqueId, DataSource } from './util';
@@ -77,7 +77,7 @@ export class RouteCalculator extends Resource implements IRouteCalculator {
7777
const parsedArn = Stack.of(scope).splitArn(routeCalculatorArn, ArnFormat.SLASH_RESOURCE_NAME);
7878

7979
if (!parsedArn.resourceName) {
80-
throw new Error(`Route Calculator Arn ${routeCalculatorArn} does not have a resource name.`);
80+
throw new UnscopedValidationError(`Route Calculator Arn ${routeCalculatorArn} does not have a resource name.`);
8181
}
8282

8383
class Import extends Resource implements IRouteCalculator {
@@ -110,26 +110,26 @@ export class RouteCalculator extends Resource implements IRouteCalculator {
110110
public readonly routeCalculatorUpdateTime: string;
111111

112112
constructor(scope: Construct, id: string, props: RouteCalculatorProps) {
113+
super(scope, id, {
114+
physicalName: props.routeCalculatorName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
115+
});
116+
// Enhanced CDK Analytics Telemetry
117+
addConstructMetadata(this, props);
118+
113119
if (props.description && !Token.isUnresolved(props.description) && props.description.length > 1000) {
114-
throw new Error(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`);
120+
throw new ValidationError(`\`description\` must be between 0 and 1000 characters. Received: ${props.description.length} characters`, this);
115121
}
116122

117123
if (props.routeCalculatorName !== undefined && !Token.isUnresolved(props.routeCalculatorName)) {
118124
if (props.routeCalculatorName.length < 1 || props.routeCalculatorName.length > 100) {
119-
throw new Error(`\`routeCalculatorName\` must be between 1 and 100 characters, got: ${props.routeCalculatorName.length} characters.`);
125+
throw new ValidationError(`\`routeCalculatorName\` must be between 1 and 100 characters, got: ${props.routeCalculatorName.length} characters.`, this);
120126
}
121127

122128
if (!/^[-._\w]+$/.test(props.routeCalculatorName)) {
123-
throw new Error(`\`routeCalculatorName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.routeCalculatorName}.`);
129+
throw new ValidationError(`\`routeCalculatorName\` must contain only alphanumeric characters, hyphens, periods and underscores, got: ${props.routeCalculatorName}.`, this);
124130
}
125131
}
126132

127-
super(scope, id, {
128-
physicalName: props.routeCalculatorName ?? Lazy.string({ produce: () => generateUniqueId(this) }),
129-
});
130-
// Enhanced CDK Analytics Telemetry
131-
addConstructMetadata(this, props);
132-
133133
const routeCalculator = new CfnRouteCalculator(this, 'Resource', {
134134
calculatorName: this.physicalName,
135135
dataSource: props.dataSource ?? DataSource.ESRI,

0 commit comments

Comments
 (0)