Skip to content

Commit ec1dd58

Browse files
authored
Merge branch 'main' into chore(rds)-marked-deprecated-version
2 parents 5a4b407 + 94be001 commit ec1dd58

10 files changed

Lines changed: 185 additions & 8 deletions

File tree

packages/@aws-cdk/aws-certificatemanager/lib/certificate.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ export class Certificate extends CertificateBase implements ICertificate {
227227
}
228228
}
229229

230+
// check if domain name is 64 characters or less
231+
if (props.domainName.length > 64) {
232+
throw new Error('Domain name must be 64 characters or less');
233+
}
234+
230235
const allDomainNames = [props.domainName].concat(props.subjectAlternativeNames || []);
231236

232237
let certificateTransparencyLoggingPreference: string | undefined;

packages/@aws-cdk/aws-certificatemanager/lib/dns-validated-certificate.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,16 @@ export class DnsValidatedCertificate extends CertificateBase implements ICertifi
8484
super(scope, id);
8585

8686
this.region = props.region;
87-
8887
this.domainName = props.domainName;
88+
// check if domain name is 64 characters or less
89+
if (this.domainName.length > 64) {
90+
throw new Error('Domain name must be 64 characters or less');
91+
}
8992
this.normalizedZoneName = props.hostedZone.zoneName;
9093
// Remove trailing `.` from zone name
9194
if (this.normalizedZoneName.endsWith('.')) {
9295
this.normalizedZoneName = this.normalizedZoneName.substring(0, this.normalizedZoneName.length - 1);
9396
}
94-
9597
// Remove any `/hostedzone/` prefix from the Hosted Zone ID
9698
this.hostedZoneId = props.hostedZone.hostedZoneId.replace(/^\/hostedzone\//, '');
9799
this.tags = new cdk.TagManager(cdk.TagType.MAP, 'AWS::CertificateManager::Certificate');

packages/@aws-cdk/aws-certificatemanager/test/certificate.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ test('can configure validation method', () => {
8181
});
8282
});
8383

84+
test('throws when domain name is longer than 64 characters', () => {
85+
const stack = new Stack();
86+
87+
expect(() => {
88+
new Certificate(stack, 'Certificate', {
89+
domainName: 'example.com'.repeat(7),
90+
});
91+
}).toThrow(/Domain name must be 64 characters or less/);
92+
});
93+
94+
8495
test('needs validation domain supplied if domain contains a token', () => {
8596
const stack = new Stack();
8697

@@ -363,4 +374,5 @@ describe('Transparency logging settings', () => {
363374
CertificateTransparencyLoggingPreference: 'DISABLED',
364375
});
365376
});
366-
});
377+
});
378+

packages/@aws-cdk/aws-certificatemanager/test/dns-validated-certificate.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,21 @@ test('works with imported role', () => {
225225
});
226226
});
227227

228+
229+
test('throws when domain name is longer than 64 characters', () => {
230+
const stack = new Stack();
231+
232+
const exampleDotComZone = new PublicHostedZone(stack, 'ExampleDotCom', {
233+
zoneName: 'example.com',
234+
});
235+
expect(() => {
236+
new DnsValidatedCertificate(stack, 'Cert', {
237+
domainName: 'example.com'.repeat(7),
238+
hostedZone: exampleDotComZone,
239+
});
240+
}).toThrow(/Domain name must be 64 characters or less/);
241+
}),
242+
228243
test('test transparency logging settings is passed to the custom resource', () => {
229244
const stack = new Stack();
230245

packages/@aws-cdk/core/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,9 @@ can be accessed from the `Fn` class, which provides type-safe methods for each
821821
intrinsic function as well as condition expressions:
822822

823823
```ts
824+
declare const myObjectOrArray: any;
825+
declare const myArray: any;
826+
824827
// To use Fn::Base64
825828
Fn.base64('SGVsbG8gQ0RLIQo=');
826829

@@ -832,6 +835,12 @@ Fn.conditionAnd(
832835
// The AWS::Region pseudo-parameter value is NOT equal to "us-east-1"
833836
Fn.conditionNot(Fn.conditionEquals('us-east-1', Aws.REGION)),
834837
);
838+
839+
// To use Fn::ToJsonString
840+
Fn.toJsonString(myObjectOrArray);
841+
842+
// To use Fn::Length
843+
Fn.len(Fn.split(',', myArray));
835844
```
836845

837846
When working with deploy-time values (those for which `Token.isUnresolved`

packages/@aws-cdk/core/lib/cfn-fn.ts

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { minimalCloudFormationJoin } from './private/cloudformation-lang';
33
import { Intrinsic } from './private/intrinsic';
44
import { Reference } from './reference';
55
import { IResolvable, IResolveContext } from './resolvable';
6+
import { Stack } from './stack';
67
import { captureStackTrace } from './stack-trace';
78
import { Token } from './token';
89

@@ -412,6 +413,37 @@ export class Fn {
412413
return Token.asList(new FnValueOfAll(parameterType, attribute));
413414
}
414415

416+
/**
417+
* The `Fn::ToJsonString` intrinsic function converts an object or array to its
418+
* corresponding JSON string.
419+
*
420+
* @param object The object or array to stringify
421+
*/
422+
public static toJsonString(object: any): string {
423+
// short-circut if object is not a token
424+
if (!Token.isUnresolved(object)) {
425+
return JSON.stringify(object);
426+
}
427+
return new FnToJsonString(object).toString();
428+
}
429+
430+
/**
431+
* The intrinsic function `Fn::Length` returns the number of elements within an array
432+
* or an intrinsic function that returns an array.
433+
*
434+
* @param array The array you want to return the number of elements from
435+
*/
436+
public static len(array: any): number {
437+
// short-circut if array is not a token
438+
if (!Token.isUnresolved(array)) {
439+
if (!Array.isArray(array)) {
440+
throw new Error('Fn.length() needs an array');
441+
}
442+
return array.length;
443+
}
444+
return Token.asNumber(new FnLength(array));
445+
}
446+
415447
private constructor() { }
416448
}
417449

@@ -829,6 +861,62 @@ class FnJoin implements IResolvable {
829861
}
830862
}
831863

864+
/**
865+
* The `Fn::ToJsonString` intrinsic function converts an object or array to its
866+
* corresponding JSON string.
867+
*/
868+
class FnToJsonString implements IResolvable {
869+
public readonly creationStack: string[];
870+
871+
private readonly object: any;
872+
873+
constructor(object: any) {
874+
this.object = object;
875+
this.creationStack = captureStackTrace();
876+
}
877+
878+
public resolve(context: IResolveContext): any {
879+
Stack.of(context.scope).addTransform('AWS::LanguageExtensions');
880+
return { 'Fn::ToJsonString': this.object };
881+
}
882+
883+
public toString() {
884+
return Token.asString(this, { displayHint: 'Fn::ToJsonString' });
885+
}
886+
887+
public toJSON() {
888+
return '<Fn::ToJsonString>';
889+
}
890+
}
891+
892+
/**
893+
* The intrinsic function `Fn::Length` returns the number of elements within an array
894+
* or an intrinsic function that returns an array.
895+
*/
896+
class FnLength implements IResolvable {
897+
public readonly creationStack: string[];
898+
899+
private readonly array: any;
900+
901+
constructor(array: any) {
902+
this.array = array;
903+
this.creationStack = captureStackTrace();
904+
}
905+
906+
public resolve(context: IResolveContext): any {
907+
Stack.of(context.scope).addTransform('AWS::LanguageExtensions');
908+
return { 'Fn::Length': this.array };
909+
}
910+
911+
public toString() {
912+
return Token.asString(this, { displayHint: 'Fn::Length' });
913+
}
914+
915+
public toJSON() {
916+
return '<Fn::Length>';
917+
}
918+
}
919+
832920
function _inGroupsOf<T>(array: T[], maxGroup: number): T[][] {
833921
const result = new Array<T[]>();
834922
for (let i = 0; i < array.length; i += maxGroup) {
@@ -843,4 +931,4 @@ function range(n: number): number[] {
843931
ret.push(i);
844932
}
845933
return ret;
846-
}
934+
}

packages/@aws-cdk/core/test/fn.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,43 @@ test('Fn.importListValue produces lists of known length', () => {
266266
]);
267267
});
268268

269+
test('Fn.toJsonString', () => {
270+
const stack = new Stack();
271+
const token = Token.asAny({ key: 'value' });
272+
273+
expect(stack.resolve(Fn.toJsonString(token))).toEqual({ 'Fn::ToJsonString': { key: 'value' } });
274+
expect(stack.templateOptions.transforms).toEqual(expect.arrayContaining([
275+
'AWS::LanguageExtensions',
276+
]));
277+
});
278+
279+
test('Fn.toJsonString with resolved value', () => {
280+
expect(Fn.toJsonString({ key: 'value' })).toEqual('{\"key\":\"value\"}');
281+
});
282+
283+
test('Fn.len', () => {
284+
const stack = new Stack();
285+
const token = Fn.split('|', Token.asString({ ThisIsASplittable: 'list' }));
286+
287+
expect(stack.resolve(Fn.len(token))).toEqual({
288+
'Fn::Length': {
289+
'Fn::Split': [
290+
'|',
291+
{
292+
ThisIsASplittable: 'list',
293+
},
294+
],
295+
},
296+
});
297+
expect(stack.templateOptions.transforms).toEqual(expect.arrayContaining([
298+
'AWS::LanguageExtensions',
299+
]));
300+
});
301+
302+
test('Fn.len with resolved value', () => {
303+
expect(Fn.len(Fn.split('|', 'a|b|c'))).toBe(3);
304+
});
305+
269306
function stringListToken(o: any): string[] {
270307
return Token.asList(new Intrinsic(o));
271308
}

packages/aws-cdk-lib/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,9 @@ can be accessed from the `Fn` class, which provides type-safe methods for each
852852
intrinsic function as well as condition expressions:
853853

854854
```ts
855+
declare const myObjectOrArray: any;
856+
declare const myArray: any;
857+
855858
// To use Fn::Base64
856859
Fn.base64('SGVsbG8gQ0RLIQo=');
857860

@@ -863,6 +866,12 @@ Fn.conditionAnd(
863866
// The AWS::Region pseudo-parameter value is NOT equal to "us-east-1"
864867
Fn.conditionNot(Fn.conditionEquals('us-east-1', Aws.REGION)),
865868
);
869+
870+
// To use Fn::ToJsonString
871+
Fn.toJsonString(myObjectOrArray);
872+
873+
// To use Fn::Length
874+
Fn.len(Fn.split(',', myArray));
866875
```
867876

868877
When working with deploy-time values (those for which `Token.isUnresolved`

packages/aws-cdk/lib/cdk-toolkit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ interface WatchOptions extends Omit<CfnDeployOptions, 'execute'> {
930930
readonly traceLogs?: boolean;
931931

932932
/**
933-
* Maximum number of simulatenous deployments (dependency permitting) to execute.
933+
* Maximum number of simultaneous deployments (dependency permitting) to execute.
934934
* The default is '1', which executes all deployments serially.
935935
*
936936
* @default 1
@@ -1009,7 +1009,7 @@ export interface DeployOptions extends CfnDeployOptions, WatchOptions {
10091009
readonly cloudWatchLogMonitor?: CloudWatchLogEventMonitor;
10101010

10111011
/**
1012-
* Maximum number of simulatenous deployments (dependency permitting) to execute.
1012+
* Maximum number of simultaneous deployments (dependency permitting) to execute.
10131013
* The default is '1', which executes all deployments serially.
10141014
*
10151015
* @default 1

packages/aws-cdk/lib/cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async function parseCommandLineArguments() {
147147
"'true' by default, use --no-logs to turn off. " +
148148
"Only in effect if specified alongside the '--watch' option",
149149
})
150-
.option('concurrency', { type: 'number', desc: 'Maximum number of simulatenous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }),
150+
.option('concurrency', { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }),
151151
)
152152
.command('import [STACK]', 'Import existing resource(s) into the given STACK', (yargs: Argv) => yargs
153153
.option('execute', { type: 'boolean', desc: 'Whether to execute ChangeSet (--no-execute will NOT execute the ChangeSet)', default: true })
@@ -217,7 +217,7 @@ async function parseCommandLineArguments() {
217217
desc: 'Show CloudWatch log events from all resources in the selected Stacks in the terminal. ' +
218218
"'true' by default, use --no-logs to turn off",
219219
})
220-
.option('concurrency', { type: 'number', desc: 'Maximum number of simulatenous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }),
220+
.option('concurrency', { type: 'number', desc: 'Maximum number of simultaneous deployments (dependency permitting) to execute.', default: 1, requiresArg: true }),
221221
)
222222
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', (yargs: Argv) => yargs
223223
.option('all', { type: 'boolean', default: false, desc: 'Destroy all available stacks' })

0 commit comments

Comments
 (0)