Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): improvements to Construct API #2767

Merged
merged 26 commits into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cpactions = require('@aws-cdk/aws-codepipeline-actions');
import iam = require('@aws-cdk/aws-iam');
import s3 = require('@aws-cdk/aws-s3');
import cdk = require('@aws-cdk/cdk');
import { ConstructNode } from '@aws-cdk/cdk';
import cxapi = require('@aws-cdk/cx-api');
import fc = require('fast-check');
import nodeunit = require('nodeunit');
Expand Down Expand Up @@ -289,7 +290,7 @@ export = nodeunit.testCase({
for (let i = 0 ; i < assetCount ; i++) {
deployedStack.node.addMetadata(cxapi.ASSET_METADATA, {});
}
test.deepEqual(action.node.validateTree().map(x => x.message),
test.deepEqual(ConstructNode.validate(action.node).map(x => x.message),
[`Cannot deploy the stack DeployedStack because it references ${assetCount} asset(s)`]);
}
)
Expand Down
1 change: 0 additions & 1 deletion packages/@aws-cdk/aws-apigateway/test/test.deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ export = {
test.done();

function synthesize() {
stack.node.prepareTree();
return SynthUtils.synthesize(stack).template;
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import targets = require('@aws-cdk/aws-events-targets');
import lambda = require('@aws-cdk/aws-lambda');
import s3 = require('@aws-cdk/aws-s3');
import sns = require('@aws-cdk/aws-sns');
import { App, CfnParameter, SecretValue, Stack } from '@aws-cdk/cdk';
import { App, CfnParameter, ConstructNode, SecretValue, Stack } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import cpactions = require('../lib');

Expand Down Expand Up @@ -47,7 +47,7 @@ export = {
});

test.notDeepEqual(SynthUtils.toCloudFormation(stack), {});
test.deepEqual([], pipeline.node.validateTree());
test.deepEqual([], ConstructNode.validate(pipeline.node));
test.done();
},

Expand Down Expand Up @@ -280,7 +280,7 @@ export = {
]
}));

test.deepEqual([], p.node.validateTree());
test.deepEqual([], ConstructNode.validate(p.node));
test.done();
},

Expand Down Expand Up @@ -371,7 +371,7 @@ export = {
]
}));

test.deepEqual([], pipeline.node.validateTree());
test.deepEqual([], ConstructNode.validate(pipeline.node));
test.done();
},

Expand Down
5 changes: 3 additions & 2 deletions packages/@aws-cdk/aws-codepipeline/test/test.artifacts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, haveResourceLike } from '@aws-cdk/assert';
import cdk = require('@aws-cdk/cdk');
import { ConstructNode } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import codepipeline = require('../lib');
import { FakeBuildAction } from './fake-build-action';
Expand Down Expand Up @@ -176,6 +177,6 @@ export = {
};

function validate(construct: cdk.IConstruct): cdk.ValidationError[] {
construct.node.prepareTree();
return construct.node.validateTree();
ConstructNode.prepare(construct.node);
return ConstructNode.validate(construct.node);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cdk = require('@aws-cdk/cdk');
import { ConstructNode } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import { IStage } from '../lib/action';
import { Artifact } from '../lib/artifact';
Expand Down Expand Up @@ -49,7 +50,7 @@ export = {
const stack = new cdk.Stack();
const pipeline = new Pipeline(stack, 'Pipeline');

test.deepEqual(pipeline.node.validateTree().length, 1);
test.deepEqual(ConstructNode.validate(pipeline.node).length, 1);

test.done();
},
Expand All @@ -68,7 +69,7 @@ export = {
],
});

test.deepEqual(pipeline.node.validateTree().length, 1);
test.deepEqual(ConstructNode.validate(pipeline.node).length, 1);

test.done();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, haveResource } from '@aws-cdk/assert';
import iam = require('@aws-cdk/aws-iam');
import { Stack, Tag } from '@aws-cdk/cdk';
import { ConstructNode, Stack, Tag } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import {
Attribute,
Expand Down Expand Up @@ -967,7 +967,7 @@ export = {
sortKey: LSI_SORT_KEY
});

const errors = table.node.validateTree();
const errors = ConstructNode.validate(table.node);

test.strictEqual(1, errors.length);
test.strictEqual('a sort key of the table must be specified to add local secondary indexes', errors[0].message);
Expand Down
8 changes: 4 additions & 4 deletions packages/@aws-cdk/aws-ec2/test/test.connections.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, haveResource } from '@aws-cdk/assert';
import { App, Stack } from '@aws-cdk/cdk';
import { App, ConstructNode, Stack } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';

import {
Expand Down Expand Up @@ -179,7 +179,7 @@ export = {
sg2.connections.allowFrom(sg1, new TcpPort(100));

// THEN -- both rules are in Stack2
app.node.prepareTree();
ConstructNode.prepare(app.node;

expect(stack2).to(haveResource('AWS::EC2::SecurityGroupIngress', {
GroupId: { "Fn::GetAtt": [ "SecurityGroupDD263621", "GroupId" ] },
Expand Down Expand Up @@ -210,7 +210,7 @@ export = {
sg2.connections.allowTo(sg1, new TcpPort(100));

// THEN -- both rules are in Stack2
app.node.prepareTree();
ConstructNode.prepare(app.node;

expect(stack2).to(haveResource('AWS::EC2::SecurityGroupIngress', {
GroupId: { "Fn::ImportValue": "Stack1:ExportsOutputFnGetAttSecurityGroupDD263621GroupIdDF6F8B09" },
Expand Down Expand Up @@ -243,7 +243,7 @@ export = {
sg2.connections.allowFrom(sg1b, new TcpPort(100));

// THEN -- both egress rules are in Stack2
app.node.prepareTree();
ConstructNode.prepare(app.node);

expect(stack2).to(haveResource('AWS::EC2::SecurityGroupEgress', {
GroupId: { "Fn::ImportValue": "Stack1:ExportsOutputFnGetAttSecurityGroupAED40ADC5GroupId1D10C76A" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export = {
});

// THEN
const errors = stack.node.validateTree();
const errors = ConstructNode.validate(stack.node);
test.deepEqual(errors.map(e => e.message), ['HTTPS Listener needs at least one certificate (call addCertificateArns)']);

test.done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import '@aws-cdk/assert/jest';
import s3 = require('@aws-cdk/aws-s3');
import sns = require('@aws-cdk/aws-sns');
import cdk = require('@aws-cdk/cdk');
import { Stack } from '@aws-cdk/cdk';
import { ConstructNode, Stack } from '@aws-cdk/cdk';
import s3n = require('../lib');

// tslint:disable:object-literal-key-quotes
Expand Down Expand Up @@ -290,7 +290,8 @@ test('a notification destination can specify a set of dependencies that must be

bucket.addObjectCreatedNotification(dest);

stack.node.prepareTree();
ConstructNode.prepare(stack.node);

expect(SynthUtils.synthesize(stack).template.Resources.BucketNotifications8F2E257D).toEqual({
Type: 'Custom::S3BucketNotifications',
Properties: {
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/cdk/lib/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface AppProps {
readonly outdir?: string;

/**
* Include stack traces in construct metadata entries.
* Include construct creation stack trace in the `aws:cdk:trace` metadata key of all constructs.
* @default true stack traces are included unless `aws:cdk:disable-stack-trace` is set in the context.
*/
readonly stackTraces?: boolean;
Expand Down Expand Up @@ -98,7 +98,7 @@ export class App extends Construct {
}

// both are reverse logic
this.runtimeInfo = this.node.getContext(cxapi.DISABLE_VERSION_REPORTING) ? false : true;
this.runtimeInfo = this.node.tryGetContext(cxapi.DISABLE_VERSION_REPORTING) ? false : true;
this.outdir = props.outdir || process.env[cxapi.OUTDIR_ENV];

const autoRun = props.autoRun !== undefined ? props.autoRun : cxapi.OUTDIR_ENV in process.env;
Expand Down
22 changes: 11 additions & 11 deletions packages/@aws-cdk/cdk/lib/cfn-element.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import cxapi = require('@aws-cdk/cx-api');
import { Construct, ConstructNode } from "./construct";
import { Token } from './token';
import { Construct } from "./construct";

const CFN_ELEMENT_SYMBOL = Symbol.for('@aws-cdk/cdk.CfnElement');

Expand Down Expand Up @@ -32,6 +31,11 @@ export abstract class CfnElement extends Construct {
*/
public readonly logicalId: string;

/**
* The stack in which this element is defined. CfnElements must be defined within a stack scope (directly or indirectly).
*/
public readonly stack: Stack;

private _logicalId: string;

/**
Expand All @@ -48,7 +52,8 @@ export abstract class CfnElement extends Construct {

this.node.addMetadata(cxapi.LOGICAL_ID_METADATA_KEY, new (require("./token").Token)(() => this.logicalId), this.constructor);

this._logicalId = this.node.stack.logicalIds.getLogicalId(this);
this.stack = Stack.of(this);
this._logicalId = this.stack.logicalIds.getLogicalId(this);
this.logicalId = new Token(() => this._logicalId, `${notTooLong(this.node.path)}.LogicalID`).toString();
}

Expand Down Expand Up @@ -87,13 +92,6 @@ export abstract class CfnElement extends Construct {
}
}

/**
* Return the path with respect to the stack
*/
public get stackPath(): string {
return this.node.ancestors(this.node.stack).map(c => c.node.id).join(ConstructNode.PATH_SEP);
}

/**
* Returns the CloudFormation 'snippet' for this entity. The snippet will only be merged
* at the root level to ensure there are no identity conflicts.
Expand Down Expand Up @@ -127,7 +125,7 @@ export abstract class CfnElement extends Construct {
// This does make the assumption that the error will not be rectified,
// but the error will be thrown later on anyway. If the error doesn't
// get thrown down the line, we may miss references.
this.node.recordReference(...findTokens(this, () => this._toCloudFormation()));
this.node.addReference(...findTokens(this, () => this._toCloudFormation()));
} catch (e) {
if (e.type !== 'CfnSynthesisError') { throw e; }
}
Expand Down Expand Up @@ -167,3 +165,5 @@ function notTooLong(x: string) {

import { CfnReference } from "./cfn-reference";
import { findTokens } from "./resolve";
import { Stack } from './stack';
import { Token } from './token';
2 changes: 1 addition & 1 deletion packages/@aws-cdk/cdk/lib/cfn-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class CfnOutput extends CfnElement {
*/
private uniqueOutputName() {
// prefix export name with stack name since exports are global within account + region.
const stackName = this.node.stack.name;
const stackName = this.stack.name;
return (stackName ? stackName + ':' : '') + this.logicalId;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/cdk/lib/cfn-reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ export class CfnReference extends Reference {
this.originalDisplayName = displayName;
this.replacementTokens = new Map<Stack, Token>();

this.producingStack = target.node.stack;
this.producingStack = Stack.of(target);
Object.defineProperty(this, CFN_REFERENCE_SYMBOL, { value: true });
}

public resolve(context: IResolveContext): any {
// If we have a special token for this consuming stack, resolve that. Otherwise resolve as if
// we are in the same stack.
const token = this.replacementTokens.get(context.scope.node.stack);
const token = this.replacementTokens.get(Stack.of(context.scope));
if (token) {
return token.resolve(context);
} else {
Expand Down
9 changes: 8 additions & 1 deletion packages/@aws-cdk/cdk/lib/cfn-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class CfnResource extends CfnRefElement {
// if aws:cdk:enable-path-metadata is set, embed the current construct's
// path in the CloudFormation template, so it will be possible to trace
// back to the actual construct path.
if (this.node.getContext(cxapi.PATH_METADATA_ENABLE_CONTEXT)) {
if (this.node.tryGetContext(cxapi.PATH_METADATA_ENABLE_CONTEXT)) {
this.options.metadata = {
[cxapi.PATH_METADATA_KEY]: this.node.path
};
Expand Down Expand Up @@ -192,6 +192,13 @@ export class CfnResource extends CfnRefElement {
this.dependsOn.add(resource);
}

/**
* @returns a string representation of this resource
*/
public toString() {
return `${super.toString()} [${this.resourceType}]`;
}

/**
* Emits CloudFormation for this resource.
* @internal
Expand Down
Loading