From 20f91c272b089a760d32cfca438b039bb2d86c9d Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Tue, 18 Jun 2019 17:30:54 +0300 Subject: [PATCH] fix(core): make IResolvable.creationStack required In order to work around https://github.com/awslabs/jsii/issues/543 we now require that anyone who implements IResolvable will implement `creationStack`. Fixes build break on master. --- packages/@aws-cdk/aws-events/lib/input.ts | 6 ++++-- packages/@aws-cdk/aws-iam/lib/policy-document.ts | 4 +++- packages/@aws-cdk/aws-iam/lib/principals.ts | 6 +++++- packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts | 4 +++- packages/@aws-cdk/cdk/lib/cfn-element.ts | 2 +- packages/@aws-cdk/cdk/lib/cfn-resource.ts | 2 +- packages/@aws-cdk/cdk/lib/construct.ts | 4 ++-- packages/@aws-cdk/cdk/lib/fn.ts | 4 ++++ packages/@aws-cdk/cdk/lib/index.ts | 1 + packages/@aws-cdk/cdk/lib/lazy.ts | 4 ++-- .../@aws-cdk/cdk/lib/private/cross-environment-token.ts | 4 ++++ packages/@aws-cdk/cdk/lib/private/intrinsic.ts | 4 ++-- packages/@aws-cdk/cdk/lib/resolvable.ts | 5 ++++- packages/@aws-cdk/cdk/lib/{private => }/stack-trace.ts | 4 ++-- packages/@aws-cdk/cdk/test/test.tokens.ts | 3 +++ 15 files changed, 41 insertions(+), 16 deletions(-) rename packages/@aws-cdk/cdk/lib/{private => }/stack-trace.ts (75%) diff --git a/packages/@aws-cdk/aws-events/lib/input.ts b/packages/@aws-cdk/aws-events/lib/input.ts index 7c089f20b0e20..7e05a1849c4c7 100644 --- a/packages/@aws-cdk/aws-events/lib/input.ts +++ b/packages/@aws-cdk/aws-events/lib/input.ts @@ -1,5 +1,5 @@ -import { DefaultTokenResolver, IResolvable, IResolveContext, - Lazy, Stack, StringConcat, Token, Tokenization } from '@aws-cdk/cdk'; +import { captureStackTrace, DefaultTokenResolver, IResolvable, + IResolveContext, Lazy, Stack, StringConcat, Token, Tokenization } from '@aws-cdk/cdk'; import { IRule } from './rule-ref'; /** @@ -274,10 +274,12 @@ export class EventField implements IResolvable { } public readonly displayHint: string; + public readonly creationStack: string[]; private constructor(public readonly path: string) { this.displayHint = this.path.replace(/^[^a-zA-Z0-9_-]+/, '').replace(/[^a-zA-Z0-9_-]/g, '-'); Object.defineProperty(this, EVENT_FIELD_SYMBOL, { value: true }); + this.creationStack = captureStackTrace(); } public resolve(_ctx: IResolveContext): any { diff --git a/packages/@aws-cdk/aws-iam/lib/policy-document.ts b/packages/@aws-cdk/aws-iam/lib/policy-document.ts index 642a13c08488c..5a96706e5c717 100644 --- a/packages/@aws-cdk/aws-iam/lib/policy-document.ts +++ b/packages/@aws-cdk/aws-iam/lib/policy-document.ts @@ -1,5 +1,5 @@ import cdk = require('@aws-cdk/cdk'); -import { IPostProcessor } from '@aws-cdk/cdk'; +import { captureStackTrace, IPostProcessor } from '@aws-cdk/cdk'; import { PolicyStatement } from './policy-statement'; /** @@ -25,10 +25,12 @@ export interface PolicyDocumentProps { * A PolicyDocument is a collection of statements */ export class PolicyDocument implements cdk.IResolvable { + public readonly creationStack: string[]; private readonly statements = new Array(); private readonly autoAssignSids: boolean; constructor(props: PolicyDocumentProps = {}) { + this.creationStack = captureStackTrace(); this.autoAssignSids = !!props.assignSids; this.addStatements(...props.statements || []); diff --git a/packages/@aws-cdk/aws-iam/lib/principals.ts b/packages/@aws-cdk/aws-iam/lib/principals.ts index 87008fd260eae..42d8bc75fd1ee 100644 --- a/packages/@aws-cdk/aws-iam/lib/principals.ts +++ b/packages/@aws-cdk/aws-iam/lib/principals.ts @@ -1,5 +1,5 @@ import cdk = require('@aws-cdk/cdk'); -import { Stack } from '@aws-cdk/cdk'; +import { captureStackTrace, Stack } from '@aws-cdk/cdk'; import { Default, RegionInfo } from '@aws-cdk/region-info'; import { PolicyStatement } from './policy-statement'; import { mergePrincipal } from './util'; @@ -312,7 +312,9 @@ export class CompositePrincipal extends PrincipalBase { * A lazy token that requires an instance of Stack to evaluate */ class StackDependentToken implements cdk.IResolvable { + public readonly creationStack: string[]; constructor(private readonly fn: (stack: cdk.Stack) => any) { + this.creationStack = captureStackTrace(); } public resolve(context: cdk.IResolveContext) { @@ -329,8 +331,10 @@ class StackDependentToken implements cdk.IResolvable { } class ServicePrincipalToken implements cdk.IResolvable { + public readonly creationStack: string[]; constructor(private readonly service: string, private readonly opts: ServicePrincipalOpts) { + this.creationStack = captureStackTrace(); } public resolve(ctx: cdk.IResolveContext) { diff --git a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts b/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts index 51bf01e370c5f..ab31ef372c37c 100644 --- a/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts +++ b/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts @@ -1,4 +1,4 @@ -import { IResolvable, IResolveContext, Token, Tokenization } from '@aws-cdk/cdk'; +import { captureStackTrace, IResolvable, IResolveContext, Token, Tokenization } from '@aws-cdk/cdk'; const JSON_PATH_TOKEN_SYMBOL = Symbol.for('@aws-cdk/aws-stepfunctions.JsonPathToken'); @@ -7,9 +7,11 @@ export class JsonPathToken implements IResolvable { return (x as any)[JSON_PATH_TOKEN_SYMBOL] === true; } + public readonly creationStack: string[]; public displayHint: string; constructor(public readonly path: string) { + this.creationStack = captureStackTrace(); this.displayHint = path.replace(/^[^a-zA-Z]+/, ''); Object.defineProperty(this, JSON_PATH_TOKEN_SYMBOL, { value: true }); } diff --git a/packages/@aws-cdk/cdk/lib/cfn-element.ts b/packages/@aws-cdk/cdk/lib/cfn-element.ts index 0f3981d51854d..25014794a8c10 100644 --- a/packages/@aws-cdk/cdk/lib/cfn-element.ts +++ b/packages/@aws-cdk/cdk/lib/cfn-element.ts @@ -77,7 +77,7 @@ export abstract class CfnElement extends Construct { * from the +metadata+ entry typed +aws:cdk:logicalId+, and with the bottom-most * node +internal+ entries filtered. */ - public get creationStackTrace(): string[] | undefined { + public get creationStack(): string[] | undefined { const trace = this.node.metadata.find(md => md.type === cxapi.LOGICAL_ID_METADATA_KEY)!.trace; if (!trace) { return undefined; diff --git a/packages/@aws-cdk/cdk/lib/cfn-resource.ts b/packages/@aws-cdk/cdk/lib/cfn-resource.ts index cc9036d9fc0cd..3fff5bc52d9ad 100644 --- a/packages/@aws-cdk/cdk/lib/cfn-resource.ts +++ b/packages/@aws-cdk/cdk/lib/cfn-resource.ts @@ -248,7 +248,7 @@ export class CfnResource extends CfnRefElement { // Change message e.message = `While synthesizing ${this.node.path}: ${e.message}`; // Adjust stack trace (make it look like node built it, too...) - const trace = this.creationStackTrace; + const trace = this.creationStack; if (trace) { const creationStack = ['--- resource created at ---', ...trace].join('\n at '); const problemTrace = e.stack.substr(e.stack.indexOf(e.message) + e.message.length); diff --git a/packages/@aws-cdk/cdk/lib/construct.ts b/packages/@aws-cdk/cdk/lib/construct.ts index b3c7ba7f6adb5..a0696ec0241c8 100644 --- a/packages/@aws-cdk/cdk/lib/construct.ts +++ b/packages/@aws-cdk/cdk/lib/construct.ts @@ -1,8 +1,8 @@ import cxapi = require('@aws-cdk/cx-api'); import { IAspect } from './aspect'; import { DependableTrait, IDependable } from './dependency'; -import { createStackTrace } from './private/stack-trace'; import { IResolvable } from './resolvable'; +import { captureStackTrace } from './stack-trace'; import { Token } from './token'; import { makeUniqueId } from './uniqueid'; @@ -312,7 +312,7 @@ export class ConstructNode { return; } - const trace = this.tryGetContext(cxapi.DISABLE_METADATA_STACK_TRACE) ? undefined : createStackTrace(from || this.addMetadata); + const trace = this.tryGetContext(cxapi.DISABLE_METADATA_STACK_TRACE) ? undefined : captureStackTrace(from || this.addMetadata); this._metadata.push({ type, data, trace }); } diff --git a/packages/@aws-cdk/cdk/lib/fn.ts b/packages/@aws-cdk/cdk/lib/fn.ts index db9e524340617..41a03d589c5af 100644 --- a/packages/@aws-cdk/cdk/lib/fn.ts +++ b/packages/@aws-cdk/cdk/lib/fn.ts @@ -2,6 +2,7 @@ import { ICfnConditionExpression } from './cfn-condition'; import { minimalCloudFormationJoin } from './cloudformation-lang'; import { Intrinsic } from './private/intrinsic'; import { IResolvable, IResolveContext } from './resolvable'; +import { captureStackTrace } from './stack-trace'; import { Token } from './token'; // tslint:disable:max-line-length @@ -630,6 +631,8 @@ class FnValueOfAll extends FnBase { * with no delimiter. */ class FnJoin implements IResolvable { + public readonly creationStack: string[]; + private readonly delimiter: string; private readonly listOfValues: any[]; // Cache for the result of resolveValues() - since it otherwise would be computed several times @@ -648,6 +651,7 @@ class FnJoin implements IResolvable { this.delimiter = delimiter; this.listOfValues = listOfValues; + this.creationStack = captureStackTrace(); } public resolve(context: IResolveContext): any { diff --git a/packages/@aws-cdk/cdk/lib/index.ts b/packages/@aws-cdk/cdk/lib/index.ts index 6bc9427edd92e..ad156c94b63d4 100644 --- a/packages/@aws-cdk/cdk/lib/index.ts +++ b/packages/@aws-cdk/cdk/lib/index.ts @@ -26,6 +26,7 @@ export * from './cfn-dynamic-reference'; export * from './tag'; export * from './removal-policy'; export * from './arn'; +export * from './stack-trace'; export * from './app'; export * from './context'; diff --git a/packages/@aws-cdk/cdk/lib/lazy.ts b/packages/@aws-cdk/cdk/lib/lazy.ts index 5c74cde9a5b9e..6c6d8ee35d99f 100644 --- a/packages/@aws-cdk/cdk/lib/lazy.ts +++ b/packages/@aws-cdk/cdk/lib/lazy.ts @@ -1,5 +1,5 @@ -import { createStackTrace } from './private/stack-trace'; import { IResolvable, IResolveContext } from "./resolvable"; +import { captureStackTrace } from './stack-trace'; import { Token } from "./token"; /** @@ -123,7 +123,7 @@ abstract class LazyBase implements IResolvable { public readonly creationStack: string[]; constructor() { - this.creationStack = createStackTrace(); + this.creationStack = captureStackTrace(); } public abstract resolve(context: IResolveContext): any; diff --git a/packages/@aws-cdk/cdk/lib/private/cross-environment-token.ts b/packages/@aws-cdk/cdk/lib/private/cross-environment-token.ts index 90e27556e52d8..9c64511f43e72 100644 --- a/packages/@aws-cdk/cdk/lib/private/cross-environment-token.ts +++ b/packages/@aws-cdk/cdk/lib/private/cross-environment-token.ts @@ -2,6 +2,7 @@ import { ArnComponents } from '../arn'; import { IResolvable, IResolveContext } from '../resolvable'; import { IResource } from '../resource'; import { Stack } from '../stack'; +import { captureStackTrace } from '../stack-trace'; /** * A Token that represents a reference that spans accounts and/or regions, @@ -11,6 +12,8 @@ import { Stack } from '../stack'; * This class is private to the @aws-cdk/cdk package. */ export abstract class CrossEnvironmentToken implements IResolvable { + public readonly creationStack: string[]; + /** * @param regularValue the value used when this is referenced NOT from a cross account and/or region Stack * @param crossEnvironmentValue the value used when this is referenced from a cross account and/or region Stack @@ -20,6 +23,7 @@ export abstract class CrossEnvironmentToken implements IResolvable { protected constructor(private readonly regularValue: string, private readonly crossEnvironmentValue: any, private readonly resource: IResource) { this.resource = resource; + this.creationStack = captureStackTrace(); } public resolve(context: IResolveContext): any { diff --git a/packages/@aws-cdk/cdk/lib/private/intrinsic.ts b/packages/@aws-cdk/cdk/lib/private/intrinsic.ts index 0c9c3c0990c25..ce2414c598058 100644 --- a/packages/@aws-cdk/cdk/lib/private/intrinsic.ts +++ b/packages/@aws-cdk/cdk/lib/private/intrinsic.ts @@ -1,6 +1,6 @@ import { IResolvable, IResolveContext } from "../resolvable"; +import { captureStackTrace } from "../stack-trace"; import { Token } from "../token"; -import { createStackTrace } from "./stack-trace"; /** * Token subclass that represents values intrinsic to the target document language @@ -25,7 +25,7 @@ export class Intrinsic implements IResolvable { throw new Error(`Argument to Intrinsic must be a plain value object, got ${value}`); } - this.creationStack = createStackTrace(); + this.creationStack = captureStackTrace(); this.value = value; } diff --git a/packages/@aws-cdk/cdk/lib/resolvable.ts b/packages/@aws-cdk/cdk/lib/resolvable.ts index b3ecf1327c386..9a3e3fa8c49b5 100644 --- a/packages/@aws-cdk/cdk/lib/resolvable.ts +++ b/packages/@aws-cdk/cdk/lib/resolvable.ts @@ -32,8 +32,11 @@ export interface IResolvable { /** * The creation stack of this resolvable which will be appended to errors * thrown during resolution. + * + * If this returns an empty array or `undefined` the stack will not be + * attached. */ - readonly creationStack?: string[]; + readonly creationStack: string[] | undefined; /** * Produce the Token's value at resolution time diff --git a/packages/@aws-cdk/cdk/lib/private/stack-trace.ts b/packages/@aws-cdk/cdk/lib/stack-trace.ts similarity index 75% rename from packages/@aws-cdk/cdk/lib/private/stack-trace.ts rename to packages/@aws-cdk/cdk/lib/stack-trace.ts index ab47cf2d47a99..0901666309875 100644 --- a/packages/@aws-cdk/cdk/lib/private/stack-trace.ts +++ b/packages/@aws-cdk/cdk/lib/stack-trace.ts @@ -1,6 +1,6 @@ // tslint:disable-next-line:ban-types -export function createStackTrace(below?: Function): string[] { - below = below || createStackTrace; // hide myself if nothing else +export function captureStackTrace(below?: Function): string[] { + below = below || captureStackTrace; // hide myself if nothing else const object = { stack: '' }; const previousLimit = Error.stackTraceLimit; try { diff --git a/packages/@aws-cdk/cdk/test/test.tokens.ts b/packages/@aws-cdk/cdk/test/test.tokens.ts index 9ad3ad18dcd25..fd43dffb7f5ba 100644 --- a/packages/@aws-cdk/cdk/test/test.tokens.ts +++ b/packages/@aws-cdk/cdk/test/test.tokens.ts @@ -602,6 +602,8 @@ export = { }; class Promise2 implements IResolvable { + public readonly creationStack = []; + public resolve() { return { Data: { @@ -614,6 +616,7 @@ class Promise2 implements IResolvable { } class Promise1 implements IResolvable { + public readonly creationStack = []; public p2 = [ new Promise2(), new Promise2() ]; public resolve() {