Skip to content

Commit 2ba5ad2

Browse files
authored
fix(core): hide dependencyRoots from public API (#2668)
`IDependable` is now a marker interface, and signals that there is a `DependableTrait` implementation which can be retrieved which contains the actual implementation of `dependencyRoots` for this instance. Fixes #2348.
1 parent 01601c2 commit 2ba5ad2

File tree

4 files changed

+83
-33
lines changed

4 files changed

+83
-33
lines changed

packages/@aws-cdk/aws-ec2/lib/vpc.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -1302,23 +1302,25 @@ function describeSelection(placement: SubnetSelection): string {
13021302
class CompositeDependable implements IDependable {
13031303
private readonly dependables = new Array<IDependable>();
13041304

1305+
constructor() {
1306+
const self = this;
1307+
cdk.DependableTrait.implement(this, {
1308+
get dependencyRoots() {
1309+
const ret = [];
1310+
for (const dep of self.dependables) {
1311+
ret.push(...cdk.DependableTrait.get(dep).dependencyRoots);
1312+
}
1313+
return ret;
1314+
}
1315+
});
1316+
}
1317+
13051318
/**
13061319
* Add a construct to the dependency roots
13071320
*/
13081321
public add(dep: IDependable) {
13091322
this.dependables.push(dep);
13101323
}
1311-
1312-
/**
1313-
* Retrieve the current set of dependency roots
1314-
*/
1315-
public get dependencyRoots(): IConstruct[] {
1316-
const ret = [];
1317-
for (const dep of this.dependables) {
1318-
ret.push(...dep.dependencyRoots);
1319-
}
1320-
return ret;
1321-
}
13221324
}
13231325

13241326
/**

packages/@aws-cdk/aws-lambda/lib/function-base.ts

-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ export abstract class FunctionBase extends Resource implements IFunction {
238238
action: 'lambda:InvokeFunction',
239239
});
240240
},
241-
dependencyRoots: [],
242241
node: this.node,
243242
},
244243
});

packages/@aws-cdk/cdk/lib/construct.ts

+8-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import cxapi = require('@aws-cdk/cx-api');
22
import { IAspect } from './aspect';
33
import { CLOUDFORMATION_TOKEN_RESOLVER, CloudFormationLang } from './cloudformation-lang';
4-
import { IDependable } from './dependency';
4+
import { DependableTrait, IDependable } from './dependency';
55
import { resolve } from './resolve';
66
import { createStackTrace } from './stack-trace';
77
import { Token } from './token';
@@ -537,7 +537,7 @@ export class ConstructNode {
537537

538538
for (const source of this.findAll()) {
539539
for (const dependable of source.node.dependencies) {
540-
for (const target of dependable.dependencyRoots) {
540+
for (const target of DependableTrait.get(dependable).dependencyRoots) {
541541
let foundTargets = found.get(source);
542542
if (!foundTargets) { found.set(source, foundTargets = new Set()); }
543543

@@ -594,14 +594,6 @@ export class Construct implements IConstruct {
594594
*/
595595
public readonly node: ConstructNode;
596596

597-
/**
598-
* The set of constructs that form the root of this dependable
599-
*
600-
* All resources under all returned constructs are included in the ordering
601-
* dependency.
602-
*/
603-
public readonly dependencyRoots: IConstruct[] = [this];
604-
605597
/**
606598
* Creates a new construct node.
607599
*
@@ -612,6 +604,12 @@ export class Construct implements IConstruct {
612604
*/
613605
constructor(scope: Construct, id: string) {
614606
this.node = new ConstructNode(this, scope, id);
607+
608+
// Implement IDependable privately
609+
const self = this;
610+
DependableTrait.implement(this, {
611+
get dependencyRoots() { return [self]; },
612+
});
615613
}
616614

617615
/**
+62-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import { IConstruct } from "./construct";
22

33
/**
4-
* A set of constructs that can be depended upon
4+
* Trait marker for classes that can be depended upon
5+
*
6+
* The presence of this interface indicates that an object has
7+
* an `IDependableTrait` implementation.
58
*
69
* This interface can be used to take an (ordering) dependency on a set of
710
* constructs. An ordering dependency implies that the resources represented by
811
* those constructs are deployed before the resources depending ON them are
912
* deployed.
1013
*/
1114
export interface IDependable {
12-
/**
13-
* The set of constructs that form the root of this dependable
14-
*
15-
* All resources under all returned constructs are included in the ordering
16-
* dependency.
17-
*/
18-
readonly dependencyRoots: IConstruct[];
15+
// Empty, this interface is a trait marker
1916
}
2017

2118
/**
@@ -27,17 +24,71 @@ export interface IDependable {
2724
export class ConcreteDependable implements IDependable {
2825
private readonly _dependencyRoots = new Array<IConstruct>();
2926

27+
constructor() {
28+
const self = this;
29+
DependableTrait.implement(this, {
30+
get dependencyRoots() { return self._dependencyRoots; },
31+
});
32+
}
33+
3034
/**
3135
* Add a construct to the dependency roots
3236
*/
3337
public add(construct: IConstruct) {
3438
this._dependencyRoots.push(construct);
3539
}
40+
}
3641

42+
/**
43+
* Trait for IDependable
44+
*
45+
* Traits are interfaces that are privately implemented by objects. Instead of
46+
* showing up in the public interface of a class, they need to be queried
47+
* explicitly. This is used to implement certain framework features that are
48+
* not intended to be used by Construct consumers, and so should be hidden
49+
* from accidental use.
50+
*
51+
* @example
52+
*
53+
* // Usage
54+
* const roots = DependableTrait.get(construct).dependencyRoots;
55+
*
56+
* // Definition
57+
* DependableTrait.implement(construct, {
58+
* get dependencyRoots() { return []; }
59+
* });
60+
*/
61+
export abstract class DependableTrait {
3762
/**
38-
* Retrieve the current set of dependency roots
63+
* Register `instance` to have the given DependableTrait
64+
*
65+
* Should be called in the class constructor.
3966
*/
40-
public get dependencyRoots(): IConstruct[] {
41-
return this._dependencyRoots;
67+
public static implement(instance: IDependable, trait: DependableTrait) {
68+
// I would also like to reference classes (to cut down on the list of objects
69+
// we need to manage), but we can't do that either since jsii doesn't have the
70+
// concept of a class reference.
71+
DependableTrait.traitMap.set(instance, trait);
4272
}
73+
74+
/**
75+
* Return the matching DependableTrait for the given class instance.
76+
*/
77+
public static get(instance: IDependable): DependableTrait {
78+
const ret = DependableTrait.traitMap.get(instance);
79+
if (!ret) {
80+
throw new Error(`${instance} does not implement DependableTrait`);
81+
}
82+
return ret;
83+
}
84+
85+
private static traitMap = new WeakMap<IDependable, DependableTrait>();
86+
87+
/**
88+
* The set of constructs that form the root of this dependable
89+
*
90+
* All resources under all returned constructs are included in the ordering
91+
* dependency.
92+
*/
93+
public abstract readonly dependencyRoots: IConstruct[];
4394
}

0 commit comments

Comments
 (0)