Skip to content

Commit

Permalink
Make lazy rendering a feature of the tag manager
Browse files Browse the repository at this point in the history
  • Loading branch information
rix0rrr committed Oct 27, 2021
1 parent 97d1331 commit c1fd796
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
19 changes: 5 additions & 14 deletions packages/@aws-cdk/aws-logs/lib/log-retention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ export class LogRetention extends CoreConstruct {
*/
class LogRetentionFunction extends CoreConstruct implements cdk.ITaggable {
public readonly functionArn: cdk.Reference;
readonly cfnFunction: cdk.CfnResource;

tags: cdk.TagManager = new cdk.TagManager(cdk.TagType.KEY_VALUE, 'AWS::Lambda::Function');

Expand All @@ -157,7 +156,7 @@ class LogRetentionFunction extends CoreConstruct implements cdk.ITaggable {
}));

// Lambda function
this.cfnFunction = new cdk.CfnResource(this, 'Resource', {
const resource = new cdk.CfnResource(this, 'Resource', {
type: 'AWS::Lambda::Function',
properties: {
Handler: 'index.handler',
Expand All @@ -167,27 +166,19 @@ class LogRetentionFunction extends CoreConstruct implements cdk.ITaggable {
S3Key: asset.s3ObjectKey,
},
Role: role.roleArn,
Tags: this.tags.renderedTags,
},
});
this.functionArn = this.cfnFunction.getAtt('Arn');
this.functionArn = resource.getAtt('Arn');

// Function dependencies
role.node.children.forEach((child) => {
if (cdk.CfnResource.isCfnResource(child)) {
this.cfnFunction.addDependsOn(child);
resource.addDependsOn(child);
}
if (cdk.Construct.isConstruct(child) && child.node.defaultChild && cdk.CfnResource.isCfnResource(child.node.defaultChild)) {
this.cfnFunction.addDependsOn(child.node.defaultChild);
resource.addDependsOn(child.node.defaultChild);
}
});
}

prepare() {
// Because we're using a CfnResource to avoid a cyclic dependency on aws-lambda
// we lose the automatic application of tags. We have to add them again manually.
// This has to be done in the prepare phase, after Tag Aspects have applied.
if (this.tags.hasTags()) {
this.cfnFunction.addPropertyOverride('Tags', this.tags.renderTags());
}
}
}
44 changes: 43 additions & 1 deletion packages/@aws-cdk/core/lib/tag-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { TagType } from './cfn-resource';
import { CfnTag } from './cfn-tag';
import { Lazy } from './lazy';
import { IResolvable } from './resolvable';

interface Tag {
key: string;
Expand Down Expand Up @@ -228,7 +230,32 @@ export interface TagManagerOptions {
}

/**
* TagManager facilitates a common implementation of tagging for Constructs.
* TagManager facilitates a common implementation of tagging for Constructs
*
* Normally, you do not need to use this class, as the CloudFormation specification
* will indicate which resources are taggable. However, sometimes you will need this
* to make custom resources taggable. Used `tagManager.renderedTags` to obtain a
* value that will resolve to the tags at synthesis time.
*
* @example
* import * as cdk from '@aws-cdk/core';
*
* class MyConstruct extends cdk.Resource implements cdk.ITaggable {
* public readonly tags = new cdk.TagManager(cdk.TagType.KEY_VALUE, 'Whatever::The::Type');
*
* constructor(scope: cdk.Construct, id: string) {
* super(scope, id);
*
* new cdk.CfnResource(this, 'Resource', {
* type: 'Whatever::The::Type',
* properties: {
* // ...
* Tags: this.tags.renderedTags,
* },
* });
* }
* }
*
*/
export class TagManager {

Expand All @@ -247,6 +274,14 @@ export class TagManager {
*/
public readonly tagPropertyName: string;

/**
* A lazy value that represents the rendered tags at synthesis time
*
* If you need to make a custom construct taggable, use the value of this
* property to pass to the `tags` property of the underlying construct.
*/
public readonly renderedTags: IResolvable;

private readonly tags = new Map<string, Tag>();
private readonly priorities = new Map<string, number>();
private readonly tagFormatter: ITagFormatter;
Expand All @@ -260,6 +295,8 @@ export class TagManager {
this._setTag(...this.tagFormatter.parseTags(tagStructure, this.initialTagPriority));
}
this.tagPropertyName = options.tagPropertyName || 'tags';

this.renderedTags = Lazy.any({ produce: () => this.renderTags() });
}

/**
Expand Down Expand Up @@ -287,6 +324,11 @@ export class TagManager {

/**
* Renders tags into the proper format based on TagType
*
* This method will eagerly render the tags currently applied. In
* most cases, you should be using `tagManager.renderedTags` instead,
* which will return a `Lazy` value that will resolve to the correct
* tags at synthesis time.
*/
public renderTags(): any {
return this.tagFormatter.formatTags(this.sortedTags);
Expand Down

0 comments on commit c1fd796

Please sign in to comment.