Skip to content

Commit 04217a5

Browse files
author
Elad Ben-Israel
authoredDec 27, 2018
feat: idiomize cloudformation intrinsics functions (#1428)
Refactor CloudFormation intrinsic functions so that they will have a more idiomatic shape. They have been converted from classes (e.g. `new FnJoin(...)`) to static methods (e.g. `Fn.join(...)`). Condition functions are mapped to `Fn.conditionXxx` and return an `FnCondition` object. Furthermore, return type of these static methods are now tokens represented as the resolved type (i.e. `string` or `string[]`) instead of `CloudFormationToken` objects. This allows using these functions and pseudo parameters naturally (instead of requiring the use of `.toString()` or `.toList()`). Fixes #202 BREAKING CHANGE: CloudFormation intrinsic functions are now represented as static methods under the `Fn` class (e.g. `Fn.join(...)` instead of `new FnJoin(...).toString()`).
1 parent 095da14 commit 04217a5

File tree

37 files changed

+541
-307
lines changed

37 files changed

+541
-307
lines changed
 

‎CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1700,7 +1700,7 @@ Fn.if_(Fn.equals(param.ref, 'True'), 'Encrypted', Pseudo.NO_VALUE)
17001700
After:
17011701
17021702
```javascript
1703-
new FnIf(new FnEquals(param.ref, 'True'), 'Encrypted', new AwsNoValue())
1703+
new FnIf(Fn.equals(param.ref, 'True'), 'Encrypted', new AwsNoValue())
17041704
```
17051705
17061706
- CloudFormation template options (`templateFormatVersion`, `description` and `transform`) are now grouped under `Stack.templateOptions` instead of directly under `Stack`.

‎docs/src/cloudformation.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ Intrinsic Functions
171171
172172
.. code-block:: js
173173
174-
import cdk = require('@aws-cdk/cdk');
175-
new cdk.FnJoin(",", [...])
174+
import { Fn } from'@aws-cdk/cdk';
175+
Fn.join(",", [...])
176176
177177
.. _pseudo_parameters:
178178

‎examples/cdk-examples-typescript/advanced-usage/index.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class CloudFormationExample extends cdk.Stack {
157157
// outputs are constructs the synthesize into the template's "Outputs" section
158158
new cdk.Output(this, 'Output', {
159159
description: 'This is an output of the template',
160-
value: new cdk.FnConcat(new cdk.AwsAccountId(), '/', param.ref)
160+
value: `${new cdk.AwsAccountId()}/${param.ref}`
161161
});
162162

163163
// stack.templateOptions can be used to specify template-level options
@@ -176,13 +176,13 @@ class CloudFormationExample extends cdk.Stack {
176176
new cdk.AwsStackName(),
177177
],
178178

179-
// all CloudFormation's intrinsic functions are supported via the `cdk.FnXxx` classes
179+
// all CloudFormation's intrinsic functions are supported via the `cdk.Fn.xxx` static methods.
180180
IntrinsicFunctions: [
181-
new cdk.FnAnd(
182-
new cdk.FnFindInMap('MyMap', 'K1', 'K2'),
183-
new cdk.FnSub('hello ${world}', {
184-
world: new cdk.FnBase64(param.ref) // resolves to { Ref: <param-id> }
185-
}))
181+
cdk.Fn.join('', [
182+
cdk.Fn.findInMap('MyMap', 'K1', 'K2'),
183+
cdk.Fn.sub('hello ${world}', {
184+
world: cdk.Fn.base64(param.ref) // resolves to { Ref: <param-id> }
185+
}) ])
186186
],
187187
};
188188
}

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"pkglint": "tools/pkglint/bin/pkglint -f ."
88
},
99
"devDependencies": {
10-
"@types/node": "^8.10.38",
10+
"@types/node": "8.10.38",
1111
"@types/nodeunit": "^0.0.30",
1212
"conventional-changelog-cli": "^2.0.5",
1313
"lerna": "^3.3.0",

‎packages/@aws-cdk/assets-docker/lib/image-asset.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ export class DockerImageAsset extends cdk.Construct {
6161
this.addMetadata(cxapi.ASSET_METADATA, asset);
6262

6363
// parse repository name and tag from the parameter (<REPO_NAME>:<TAG>)
64-
const components = new cdk.FnSplit(':', imageNameParameter.value);
65-
const repositoryName = new cdk.FnSelect(0, components).toString();
66-
const imageTag = new cdk.FnSelect(1, components).toString();
64+
const components = cdk.Fn.split(':', imageNameParameter.valueAsString);
65+
const repositoryName = cdk.Fn.select(0, components).toString();
66+
const imageTag = cdk.Fn.select(1, components).toString();
6767

6868
// Require that repository adoption happens first, so we route the
6969
// input ARN into the Custom Resource and then get the URI which we use to

‎packages/@aws-cdk/assets/lib/asset.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ export class Asset extends cdk.Construct {
110110
});
111111

112112
this.s3BucketName = bucketParam.value.toString();
113-
this.s3Prefix = new cdk.FnSelect(0, new cdk.FnSplit(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.value)).toString();
114-
const s3Filename = new cdk.FnSelect(1, new cdk.FnSplit(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.value)).toString();
113+
this.s3Prefix = cdk.Fn.select(0, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString();
114+
const s3Filename = cdk.Fn.select(1, cdk.Fn.split(cxapi.ASSET_PREFIX_SEPARATOR, keyParam.valueAsString)).toString();
115115
this.s3ObjectKey = `${this.s3Prefix}${s3Filename}`;
116116

117117
this.bucket = s3.BucketRef.import(this, 'AssetBucket', {

‎packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export class AutoScalingGroup extends cdk.Construct implements IAutoScalingGroup
222222

223223
// use delayed evaluation
224224
const machineImage = props.machineImage.getImage(this);
225-
const userDataToken = new cdk.Token(() => new cdk.FnBase64((machineImage.os.createUserData(this.userDataLines))));
225+
const userDataToken = new cdk.Token(() => cdk.Fn.base64((machineImage.os.createUserData(this.userDataLines))));
226226
const securityGroupsToken = new cdk.Token(() => this.securityGroups.map(sg => sg.securityGroupId));
227227

228228
const launchConfig = new CfnLaunchConfiguration(this, 'LaunchConfig', {

‎packages/@aws-cdk/aws-cloudfront/lib/web_distribution.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -574,9 +574,7 @@ export class CloudFrontWebDistribution extends cdk.Construct implements route53.
574574

575575
if (originConfig.s3OriginSource && originConfig.s3OriginSource.originAccessIdentity) {
576576
originProperty.s3OriginConfig = {
577-
originAccessIdentity: new cdk.FnConcat(
578-
"origin-access-identity/cloudfront/", originConfig.s3OriginSource.originAccessIdentity.ref
579-
),
577+
originAccessIdentity: `origin-access-identity/cloudfront/${originConfig.s3OriginSource.originAccessIdentity.ref}`
580578
};
581579
} else if (originConfig.s3OriginSource) {
582580
originProperty.s3OriginConfig = {};

‎packages/@aws-cdk/aws-cloudtrail/lib/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class CloudTrail extends cdk.Construct {
138138
.addServicePrincipal(cloudTrailPrincipal));
139139

140140
s3bucket.addToResourcePolicy(new iam.PolicyStatement()
141-
.addResource(s3bucket.arnForObjects(new cdk.FnConcat('AWSLogs/', new cdk.AwsAccountId(), "/*")))
141+
.addResource(s3bucket.arnForObjects(`AWSLogs/${new cdk.AwsAccountId()}/*`))
142142
.addActions("s3:PutObject")
143143
.addServicePrincipal(cloudTrailPrincipal)
144144
.setCondition("StringEquals", {'s3:x-amz-acl': "bucket-owner-full-control"}));

‎packages/@aws-cdk/aws-codebuild/lib/project.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -495,10 +495,10 @@ export class Project extends ProjectRef {
495495

496496
let cache: CfnProject.ProjectCacheProperty | undefined;
497497
if (props.cacheBucket) {
498-
const cacheDir = props.cacheDir != null ? props.cacheDir : new cdk.AwsNoValue();
498+
const cacheDir = props.cacheDir != null ? props.cacheDir : new cdk.AwsNoValue().toString();
499499
cache = {
500500
type: 'S3',
501-
location: new cdk.FnJoin('/', [props.cacheBucket.bucketName, cacheDir]),
501+
location: cdk.Fn.join('/', [props.cacheBucket.bucketName, cacheDir]),
502502
};
503503

504504
props.cacheBucket.grantReadWrite(this.role);

‎packages/@aws-cdk/aws-codebuild/test/test.codebuild.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -855,14 +855,14 @@ export = {
855855
new codebuild.Project(stack, 'Project', {
856856
source: new codebuild.CodePipelineSource(),
857857
environment: {
858-
environmentVariables: {
859-
FOO: { value: '1234' },
860-
BAR: { value: new cdk.FnConcat('111', { twotwotwo: '222' }), type: codebuild.BuildEnvironmentVariableType.ParameterStore }
861-
}
858+
environmentVariables: {
859+
FOO: { value: '1234' },
860+
BAR: { value: `111${new cdk.CloudFormationToken({ twotwotwo: '222' })}`, type: codebuild.BuildEnvironmentVariableType.ParameterStore }
861+
}
862862
},
863863
environmentVariables: {
864-
GOO: { value: 'ABC' },
865-
FOO: { value: 'OVERRIDE!' }
864+
GOO: { value: 'ABC' },
865+
FOO: { value: 'OVERRIDE!' }
866866
}
867867
});
868868

‎packages/@aws-cdk/aws-codecommit/lib/repository.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,7 @@ class ImportedRepositoryRef extends RepositoryRef {
196196
}
197197

198198
private repositoryCloneUrl(protocol: 'https' | 'ssh'): string {
199-
return new cdk.FnConcat(`${protocol}://git-codecommit.`,
200-
new cdk.AwsRegion(),
201-
'.',
202-
new cdk.AwsURLSuffix(),
203-
'/v1/repos/',
204-
this.repositoryName).toString();
199+
return `${protocol}://git-codecommit.${new cdk.AwsRegion()}.${new cdk.AwsURLSuffix()}/v1/repos/${this.repositoryName}`;
205200
}
206201
}
207202

‎packages/@aws-cdk/aws-ecr/test/test.repository.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export = {
192192

193193
// WHEN/THEN
194194
test.throws(() => ecr.Repository.import(stack, 'Repo', {
195-
repositoryArn: new cdk.FnGetAtt('Boom', 'Boom').toString()
195+
repositoryArn: cdk.Fn.getAtt('Boom', 'Boom').toString()
196196
}), /repositoryArn is a late-bound value, and therefore repositoryName is required/);
197197

198198
test.done();
@@ -204,8 +204,8 @@ export = {
204204

205205
// WHEN
206206
const repo = ecr.Repository.import(stack, 'Repo', {
207-
repositoryArn: new cdk.FnGetAtt('Boom', 'Arn').toString(),
208-
repositoryName: new cdk.FnGetAtt('Boom', 'Name').toString()
207+
repositoryArn: cdk.Fn.getAtt('Boom', 'Arn').toString(),
208+
repositoryName: cdk.Fn.getAtt('Boom', 'Name').toString()
209209
});
210210

211211
// THEN
@@ -242,7 +242,7 @@ export = {
242242
'arnForLocalRepository can be used to render an ARN for a local repository'(test: Test) {
243243
// GIVEN
244244
const stack = new cdk.Stack();
245-
const repoName = new cdk.FnGetAtt('Boom', 'Name').toString();
245+
const repoName = cdk.Fn.getAtt('Boom', 'Name').toString();
246246

247247
// WHEN
248248
const repo = ecr.Repository.import(stack, 'Repo', {

‎packages/@aws-cdk/aws-elasticloadbalancingv2/lib/shared/base-target-group.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,6 @@ export interface LoadBalancerTargetProps {
373373
* app/my-load-balancer/50dc6c495c0c9188
374374
*/
375375
export function loadBalancerNameFromListenerArn(listenerArn: string) {
376-
const arnParts = new cdk.FnSplit('/', listenerArn);
377-
return `${new cdk.FnSelect(1, arnParts)}/${new cdk.FnSelect(2, arnParts)}/${new cdk.FnSelect(3, arnParts)}`;
376+
const arnParts = cdk.Fn.split('/', listenerArn);
377+
return `${cdk.Fn.select(1, arnParts)}/${cdk.Fn.select(2, arnParts)}/${cdk.Fn.select(3, arnParts)}`;
378378
}

‎packages/@aws-cdk/aws-events/lib/input-options.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ export interface TargetInputTemplate {
1616
* @example
1717
*
1818
* {
19-
* textTemplate: 'Build <buildid> started',
20-
* pathsMap: {
21-
* buildid: '$.detail.id'
22-
* }
19+
* textTemplate: 'Build <buildid> started',
20+
* pathsMap: {
21+
* buildid: '$.detail.id'
22+
* }
2323
* }
2424
*/
25-
textTemplate?: any;
25+
textTemplate?: string;
2626

2727
/**
2828
* Input template where you can use the values of the keys from

‎packages/@aws-cdk/aws-events/lib/rule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Construct, FnConcat, Token } from '@aws-cdk/cdk';
1+
import { Construct, Token } from '@aws-cdk/cdk';
22
import { EventPattern } from './event-pattern';
33
import { CfnRule } from './events.generated';
44
import { TargetInputTemplate } from './input-options';
@@ -133,7 +133,7 @@ export class EventRule extends EventRuleRef {
133133
} else if (typeof(inputOptions.textTemplate) === 'string') {
134134
inputTemplate = JSON.stringify(inputOptions.textTemplate);
135135
} else {
136-
inputTemplate = new FnConcat('"', inputOptions.textTemplate, '"');
136+
inputTemplate = `"${inputOptions.textTemplate}"`;
137137
}
138138

139139
return {

‎packages/@aws-cdk/aws-events/test/test.rule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export = {
238238
// tokens are used here (FnConcat), but this is a text template so we
239239
// expect it to be wrapped in double quotes automatically for us.
240240
rule.addTarget(t1, {
241-
textTemplate: new cdk.FnConcat('a', 'b')
241+
textTemplate: cdk.Fn.join('', [ 'a', 'b' ]).toString()
242242
});
243243

244244
// jsonTemplate can be used to format JSON documents with replacements
@@ -252,7 +252,7 @@ export = {
252252
// tokens can also used for JSON templates, but that means escaping is
253253
// the responsibility of the user.
254254
rule.addTarget(t4, {
255-
jsonTemplate: new cdk.FnJoin(' ', ['"', 'hello', '\"world\"', '"']),
255+
jsonTemplate: cdk.Fn.join(' ', ['"', 'hello', '\"world\"', '"']),
256256
});
257257

258258
expect(stack).toMatch({

‎packages/@aws-cdk/aws-iam/test/test.policy-document.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FnConcat, resolve } from '@aws-cdk/cdk';
1+
import { resolve, Token } from '@aws-cdk/cdk';
22
import { Test } from 'nodeunit';
33
import { Anyone, AnyPrincipal, CanonicalUserPrincipal, PolicyDocument, PolicyPrincipal, PolicyStatement } from '../lib';
44
import { ArnPrincipal, CompositePrincipal, FederatedPrincipal, PrincipalPolicyFragment, ServicePrincipal } from '../lib';
@@ -12,7 +12,7 @@ export = {
1212
p.addResource('yourQueue');
1313

1414
p.addAllResources();
15-
p.addAwsAccountPrincipal(new FnConcat('my', { account: 'account' }, 'name').toString());
15+
p.addAwsAccountPrincipal(`my${new Token({ account: 'account' })}name`);
1616
p.limitToAccount('12221121221');
1717

1818
test.deepEqual(resolve(p), { Action:

‎packages/@aws-cdk/aws-kinesis/lib/stream.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export abstract class StreamRef extends cdk.Construct implements logs.ILogSubscr
170170
if (!this.cloudWatchLogsRole) {
171171
// Create a role to be assumed by CWL that can write to this stream and pass itself.
172172
this.cloudWatchLogsRole = new iam.Role(this, 'CloudWatchLogsCanPutRecords', {
173-
assumedBy: new iam.ServicePrincipal(new cdk.FnConcat('logs.', new cdk.AwsRegion(), '.amazonaws.com').toString()),
173+
assumedBy: new iam.ServicePrincipal(`logs.${new cdk.AwsRegion()}.amazonaws.com`)
174174
});
175175
this.cloudWatchLogsRole.addToPolicy(new iam.PolicyStatement().addAction('kinesis:PutRecord').addResource(this.streamArn));
176176
this.cloudWatchLogsRole.addToPolicy(new iam.PolicyStatement().addAction('iam:PassRole').addResource(this.cloudWatchLogsRole.roleArn));

‎packages/@aws-cdk/aws-lambda/lib/lambda-ref.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export abstract class FunctionRef extends cdk.Construct
320320
//
321321
// (Wildcards in principals are unfortunately not supported.
322322
this.addPermission('InvokedByCloudWatchLogs', {
323-
principal: new iam.ServicePrincipal(new cdk.FnConcat('logs.', new cdk.AwsRegion(), '.amazonaws.com').toString()),
323+
principal: new iam.ServicePrincipal(`logs.${new cdk.AwsRegion()}.amazonaws.com`),
324324
sourceArn: arn
325325
});
326326
this.logSubscriptionDestinationPolicyAddedFor.push(arn);
@@ -451,6 +451,6 @@ class LambdaRefImport extends FunctionRef {
451451
* @returns `FnSelect(6, FnSplit(':', arn))`
452452
*/
453453
private extractNameFromArn(arn: string) {
454-
return new cdk.FnSelect(6, new cdk.FnSplit(':', arn)).toString();
454+
return cdk.Fn.select(6, cdk.Fn.split(':', arn)).toString();
455455
}
456456
}

‎packages/@aws-cdk/aws-s3/lib/bucket.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ export abstract class BucketRef extends cdk.Construct {
165165
* bucket is returned.
166166
* @returns an ObjectS3Url token
167167
*/
168-
public urlForObject(key?: any): string {
169-
const components = [ 'https://', 's3.', new cdk.AwsRegion(), '.', new cdk.AwsURLSuffix(), '/', this.bucketName ];
168+
public urlForObject(key?: string): string {
169+
const components = [ `https://s3.${new cdk.AwsRegion()}.${new cdk.AwsURLSuffix()}/${this.bucketName}` ];
170170
if (key) {
171171
// trim prepending '/'
172172
if (typeof key === 'string' && key.startsWith('/')) {
@@ -176,7 +176,7 @@ export abstract class BucketRef extends cdk.Construct {
176176
components.push(key);
177177
}
178178

179-
return new cdk.FnConcat(...components).toString();
179+
return components.join('');
180180
}
181181

182182
/**
@@ -188,8 +188,8 @@ export abstract class BucketRef extends cdk.Construct {
188188
* arnForObjects('home/', team, '/', user, '/*')
189189
*
190190
*/
191-
public arnForObjects(...keyPattern: any[]): string {
192-
return new cdk.FnConcat(this.bucketArn, '/', ...keyPattern).toString();
191+
public arnForObjects(...keyPattern: string[]): string {
192+
return `${this.bucketArn}/${keyPattern.join('')}`;
193193
}
194194

195195
/**

‎packages/@aws-cdk/aws-s3/test/test.util.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cdk = require('@aws-cdk/cdk');
2+
import { CloudFormationToken } from '@aws-cdk/cdk';
23
import { Test } from 'nodeunit';
34
import { parseBucketArn, parseBucketName } from '../lib/util';
45

@@ -41,7 +42,7 @@ export = {
4142
},
4243

4344
'undefined if cannot extract name from a non-string arn'(test: Test) {
44-
const bucketArn = new cdk.FnConcat('arn:aws:s3:::', { Ref: 'my-bucket' }).toString();
45+
const bucketArn = `arn:aws:s3:::${new CloudFormationToken({ Ref: 'my-bucket' })}`;
4546
test.deepEqual(cdk.resolve(parseBucketName({ bucketArn })), undefined);
4647
test.done();
4748
},

0 commit comments

Comments
 (0)