Skip to content

Commit

Permalink
feat(cfn2ts) Add support for Tags on L1 constructs (aws#1007)
Browse files Browse the repository at this point in the history
 * If an L1 Resource has a property `Tags` then add support for a
 TagManager
  • Loading branch information
moofish32 committed Oct 28, 2018
1 parent c87f3ec commit d9a0bc5
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
6 changes: 1 addition & 5 deletions packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,7 @@ export class VpcNetwork extends VpcNetworkRef implements cdk.ITaggable {

// Create an Internet Gateway and attach it if necessary
if (allowOutbound) {
const igw = new cloudformation.InternetGatewayResource(this, 'IGW', {
tags: new cdk.TagManager(this),
});
const igw = new cloudformation.InternetGatewayResource(this, 'IGW');
const att = new cloudformation.VPCGatewayAttachmentResource(this, 'VPCGW', {
internetGatewayId: igw.ref,
vpcId: this.resource.ref
Expand Down Expand Up @@ -499,7 +497,6 @@ export class VpcSubnet extends VpcSubnetRef implements cdk.ITaggable {
this.subnetId = subnet.subnetId;
const table = new cloudformation.RouteTableResource(this, 'RouteTable', {
vpcId: props.vpcId,
tags: new cdk.TagManager(this),
});
this.routeTableId = table.ref;

Expand Down Expand Up @@ -556,7 +553,6 @@ export class VpcPublicSubnet extends VpcSubnet {
allocationId: new cloudformation.EIPResource(this, `EIP`, {
domain: 'vpc'
}).eipAllocationId,
tags: new cdk.TagManager(this),
});
return ngw.natGatewayId;
}
Expand Down
48 changes: 41 additions & 7 deletions tools/cfn2ts/lib/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { itemTypeNames, PropertyAttributeName, scalarTypeNames, SpecName } from
const CORE = genspec.CORE_NAMESPACE;
const RESOURCE_BASE_CLASS = `${CORE}.Resource`; // base class for all resources
const CONSTRUCT_CLASS = `${CORE}.Construct`;
const TAGGABLE_INTERFACE = `${CORE}.ITaggable`;

interface Dictionary<T> { [key: string]: T; }

Expand Down Expand Up @@ -106,10 +107,11 @@ export default class CodeGenerator {
}
}

private openClass(name: genspec.CodeName, docLink?: string, superClasses?: string) {
private openClass(name: genspec.CodeName, docLink?: string, interfaces?: string[], superClasses?: string) {
const extendsPostfix = superClasses ? ` extends ${superClasses}` : '';
const implementPostfix = interfaces ? ` implements ${interfaces.join(', ')}` : '';
this.docLink(docLink);
this.code.openBlock(`export class ${name.className}${extendsPostfix}`);
this.code.openBlock(`export class ${name.className}${extendsPostfix}${implementPostfix}`);
return name.className;
}

Expand Down Expand Up @@ -183,7 +185,9 @@ export default class CodeGenerator {
if (propsType) {
this.code.line();
}
this.openClass(resourceName, spec.Documentation, RESOURCE_BASE_CLASS);

const implInterfaces: string[] | undefined = isTaggable(spec) ? [TAGGABLE_INTERFACE] : undefined;
this.openClass(resourceName, spec.Documentation, implInterfaces, RESOURCE_BASE_CLASS);

//
// Static inspectors.
Expand Down Expand Up @@ -238,6 +242,16 @@ export default class CodeGenerator {
}
}

//
// ITaggable required properties
//
if (isTaggable(spec)) {
this.code.line('/**');
this.code.line(' * Tag Manager for resoure');
this.code.line(' */');
this.code.line('public readonly tags: cdk.TagManager;');
}

//
// Constructor
//
Expand Down Expand Up @@ -286,11 +300,16 @@ export default class CodeGenerator {
}
}

// initialize tag manager
if (isTaggable(spec)) {
this.code.line(`this.tags = new ${CORE}.TagManager(parent);`);
}

this.code.closeBlock();

if (propsType) {
this.code.line();
this.emitCloudFormationPropertiesOverride(propsType);
this.emitCloudFormationPropertiesOverride(propsType, isTaggable(spec));
}

this.closeClass(resourceName);
Expand All @@ -305,13 +324,23 @@ export default class CodeGenerator {
*
* Since resolve() deep-resolves, we only need to do this once.
*/
private emitCloudFormationPropertiesOverride(propsType: genspec.CodeName) {
private emitCloudFormationPropertiesOverride(propsType: genspec.CodeName, taggable: boolean) {
this.code.openBlock(`public get propertyOverrides(): ${propsType.className}`);
this.code.line(`return this.untypedPropertyOverrides;`);
this.code.closeBlock();
this.code.line();

this.code.openBlock('protected renderProperties(properties: any): { [key: string]: any } ');
this.code.line(`return ${genspec.cfnMapperName(propsType).fqn}(${CORE}.resolve(properties));`);
this.code.line(`const resolvedProps = ${CORE}.resolve(properties);`);
if (taggable) {
this.code.line(`const rawTags: ${CORE}.Tag[] = resolvedProps.tags || [];`);
this.code.line(`const managedTags: ${CORE}.Tag[] = ${CORE}.resolve(this.tags) || [];`);
this.code.line(`const rawTagKeys: string[] = rawTags.map(t => t.key);`);
this.code.line(`// order is important L1 (raw) tags override managed tags`);
this.code.line(`const uniqueTags = rawTags.concat(managedTags.filter(t => rawTagKeys.indexOf(t.key) < 0));`);
this.code.line(`resolvedProps.tags = uniqueTags.length !== 0 ? uniqueTags : undefined;`);
}
this.code.line(`return ${genspec.cfnMapperName(propsType).fqn}(resolvedProps);`);
this.code.closeBlock();
}

Expand Down Expand Up @@ -474,7 +503,8 @@ export default class CodeGenerator {
}

this.code.line();
this.openClass(attr.typeName, attr.docLink, attr.baseClassName.fqn);
this.openClass(attr.typeName, attr.docLink, undefined, attr.baseClassName.fqn);

// Add a private member that will make the class structurally
// different in TypeScript, which prevents assigning returning
// incorrectly-typed Tokens. Those will cause ClassCastExceptions
Expand Down Expand Up @@ -628,3 +658,7 @@ function mapperNames(types: genspec.CodeName[]): string {
function quoteCode(code: string): string {
return '``' + code + '``';
}

function isTaggable(resource: schema.ResourceType): boolean {
return !!resource.Properties && !!resource.Properties.Tags;
}

0 comments on commit d9a0bc5

Please sign in to comment.