Skip to content

Commit bb7f237

Browse files
authoredOct 11, 2024
fix: cannot create root construct in python (#2496)
Fixes #1063
1 parent 75f29f4 commit bb7f237

File tree

3 files changed

+75
-40
lines changed

3 files changed

+75
-40
lines changed
 

‎API.md

+25-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/construct.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ export class Node {
9595
* Addresses are calculated using a SHA-1 of the components of the construct
9696
* path.
9797
*
98-
* To enable refactorings of construct trees, constructs with the ID `Default`
98+
* To enable refactoring of construct trees, constructs with the ID `Default`
9999
* will be excluded from the calculation. In those cases constructs in the
100-
* same tree may have the same addreess.
100+
* same tree may have the same address.
101101
*
102102
* @example c83a2846e506bcc5f10682b564084bca2d275709ee
103103
*/
@@ -465,6 +465,18 @@ export class Construct implements IConstruct {
465465
return x && typeof x === 'object' && x[CONSTRUCT_SYM];
466466
}
467467

468+
/**
469+
* Creates a new root construct node.
470+
*
471+
* @param id The scoped construct ID. Must be unique amongst siblings. If
472+
* the ID includes a path separator (`/`), then it will be replaced by double
473+
* dash `--`.
474+
*/
475+
public static createRoot(id?: string): Construct {
476+
return new Construct(undefined as any, id ?? '');
477+
}
478+
479+
468480
/**
469481
* The tree node.
470482
*/

‎test/construct.test.ts

+36-36
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { Construct, ConstructOrder, DependencyGroup, Dependable, IConstruct } fr
55
// tslint:disable:max-line-length
66

77
test('the "Root" construct is a special construct which can be used as the root of the tree', () => {
8-
const root = new Root();
8+
const root = Construct.createRoot();
99
const node = root.node;
1010
expect(node.id).toBe('');
1111
expect(node.scope).toBeUndefined();
1212
expect(node.children.length).toBe(0);
1313
});
1414

1515
test('an empty string is a valid name for the root construct', () => {
16-
const root = new Root();
16+
const root = Construct.createRoot();
1717
expect(root.node.id).toEqual('');
1818

1919
expect(() => new Construct(root, '')).toThrow(/Only root constructs/);
@@ -32,7 +32,7 @@ test('construct.name returns the name of the construct', () => {
3232
});
3333

3434
test('construct id can use any character except the path separator', () => {
35-
const root = new Root();
35+
const root = Construct.createRoot();
3636
expect(() => new Construct(root, 'valid')).not.toThrow();
3737
expect(() => new Construct(root, 'ValiD')).not.toThrow();
3838
expect(() => new Construct(root, 'Va123lid')).not.toThrow();
@@ -48,7 +48,7 @@ test('construct id can use any character except the path separator', () => {
4848
});
4949

5050
test('if construct id contains path seperators, they will be replaced by double-dash', () => {
51-
const root = new Root();
51+
const root = Construct.createRoot();
5252
const c = new Construct(root, 'Boom/Boom/Bam');
5353
expect(c.node.id).toBe('Boom--Boom--Bam');
5454
});
@@ -59,7 +59,7 @@ test('if "undefined" is forcefully used as an "id", it will be treated as an emp
5959
});
6060

6161
test('node.addr returns an opaque app-unique address for any construct', () => {
62-
const root = new Root();
62+
const root = Construct.createRoot();
6363

6464
const child1 = new Construct(root, 'This is the first child');
6565
const child2 = new Construct(child1, 'Second level');
@@ -76,7 +76,7 @@ test('node.addr returns an opaque app-unique address for any construct', () => {
7676

7777
test('node.addr excludes "default" from the address calculation', () => {
7878
// GIVEN
79-
const root = new Root();
79+
const root = Construct.createRoot();
8080
const c1 = new Construct(root, 'c1');
8181

8282
// WHEN:
@@ -97,23 +97,23 @@ test('node.addr excludes "default" from the address calculation', () => {
9797
});
9898

9999
test('construct.getChildren() returns an array of all children', () => {
100-
const root = new Root();
100+
const root = Construct.createRoot();
101101
const child = new Construct(root, 'Child1');
102102
new Construct(root, 'Child2');
103103
expect(child.node.children.length).toBe(0);
104104
expect(root.node.children.length).toBe(2);
105105
});
106106

107107
test('construct.findChild(name) can be used to retrieve a child from a parent', () => {
108-
const root = new Root();
109-
const child = new Construct(root, 'Contruct');
108+
const root = Construct.createRoot();
109+
const child = new Construct(root, 'Construct');
110110
expect(root.node.tryFindChild(child.node.id)).toBe(child);
111111
expect(root.node.tryFindChild('NotFound')).toBeUndefined();
112112
});
113113

114114
test('construct.getChild(name) can be used to retrieve a child from a parent', () => {
115-
const root = new Root();
116-
const child = new Construct(root, 'Contruct');
115+
const root = Construct.createRoot();
116+
const child = new Construct(root, 'Construct');
117117
expect(root.node.findChild(child.node.id)).toBe(child);
118118
expect(() => root.node.findChild('NotFound')).toThrow(/No child with id: 'NotFound'/);
119119
});
@@ -158,7 +158,7 @@ test('construct.getAllContext can be used to read the full context of a root nod
158158
};
159159

160160
// WHEN
161-
const t = new Root();
161+
const t = Construct.createRoot();
162162
for (const [k, v] of Object.entries(context)) {
163163
t.node.setContext(k, v);
164164
}
@@ -196,7 +196,7 @@ test('construct.tryGetContext(key) can be used to read a value from context defi
196196

197197
// tslint:disable-next-line:max-line-length
198198
test('construct.setContext(k,v) sets context at some level and construct.tryGetContext(key) will return the lowermost value defined in the stack', () => {
199-
const root = new Root();
199+
const root = Construct.createRoot();
200200
const highChild = new Construct(root, 'highChild');
201201
highChild.node.setContext('c1', 'root');
202202
highChild.node.setContext('c2', 'root');
@@ -229,7 +229,7 @@ test('construct.setContext(k,v) sets context at some level and construct.tryGetC
229229
});
230230

231231
test('construct.setContext(key, value) can only be called before adding any children', () => {
232-
const root = new Root();
232+
const root = Construct.createRoot();
233233
new Construct(root, 'child1');
234234
expect(() => root.node.setContext('k', 'v')).toThrow(/Cannot set context after children have been added: child1/);
235235
});
@@ -265,7 +265,7 @@ test('construct can not be created with the name of a sibling', () => {
265265
});
266266

267267
test('addMetadata(type, data) can be used to attach metadata to constructs', () => {
268-
const root = new Root();
268+
const root = Construct.createRoot();
269269
const con = new Construct(root, 'MyConstruct');
270270
expect(con.node.metadata).toEqual([]);
271271

@@ -285,7 +285,7 @@ test('addMetadata(type, data) can be used to attach metadata to constructs', ()
285285
});
286286

287287
test('addMetadata() respects the "stackTrace" option', () => {
288-
const root = new Root();
288+
const root = Construct.createRoot();
289289
const con = new Construct(root, 'Foo');
290290

291291
con.node.addMetadata('foo', 'bar1', { stackTrace: true });
@@ -297,7 +297,7 @@ test('addMetadata() respects the "stackTrace" option', () => {
297297
});
298298

299299
test('addMetadata(type, undefined/null) is ignored', () => {
300-
const root = new Root();
300+
const root = Construct.createRoot();
301301
const con = new Construct(root, 'Foo');
302302
const node = con.node;
303303
node.addMetadata('Null', null);
@@ -316,7 +316,7 @@ test('addMetadata(type, undefined/null) is ignored', () => {
316316
});
317317

318318
test('multiple children of the same type, with explicit names are welcome', () => {
319-
const root = new Root();
319+
const root = Construct.createRoot();
320320
new MyBeautifulConstruct(root, 'mbc1');
321321
new MyBeautifulConstruct(root, 'mbc2');
322322
new MyBeautifulConstruct(root, 'mbc3');
@@ -392,15 +392,15 @@ test('node.addValidation() can be implemented to perform validation, node.valida
392392

393393
test('node.validate() returns an empty array if the construct does not implement IValidation', () => {
394394
// GIVEN
395-
const root = new Root();
395+
const root = Construct.createRoot();
396396

397397
// THEN
398398
expect(root.node.validate()).toStrictEqual([]);
399399
});
400400

401401
test('node.addValidation() can be used to add a validation function to a construct', () => {
402402
// GIVEN
403-
const construct = new Root();
403+
const construct = Construct.createRoot();
404404
construct.node.addValidation({ validate: () => ['error1', 'error2'] });
405405
construct.node.addValidation({ validate: () => ['error3'] });
406406

@@ -409,7 +409,7 @@ test('node.addValidation() can be used to add a validation function to a constru
409409

410410
test('construct.lock() protects against adding children anywhere under this construct (direct or indirect)', () => {
411411

412-
const root = new Root();
412+
const root = Construct.createRoot();
413413

414414
const c0a = new Construct(root, 'c0a');
415415
const c0b = new Construct(root, 'c0b');
@@ -461,38 +461,38 @@ test('"root" returns the root construct', () => {
461461

462462
describe('defaultChild', () => {
463463
test('returns the child with id "Resource"', () => {
464-
const root = new Root();
464+
const root = Construct.createRoot();
465465
new Construct(root, 'child1');
466466
const defaultChild = new Construct(root, 'Resource');
467467
new Construct(root, 'child2');
468468

469469
expect(root.node.defaultChild).toBe(defaultChild);
470470
});
471471
test('returns the child with id "Default"', () => {
472-
const root = new Root();
472+
const root = Construct.createRoot();
473473
new Construct(root, 'child1');
474474
const defaultChild = new Construct(root, 'Default');
475475
new Construct(root, 'child2');
476476

477477
expect(root.node.defaultChild).toBe(defaultChild);
478478
});
479479
test('can override defaultChild', () => {
480-
const root = new Root();
480+
const root = Construct.createRoot();
481481
new Construct(root, 'Resource');
482482
const defaultChild = new Construct(root, 'OtherResource');
483483
root.node.defaultChild = defaultChild;
484484

485485
expect(root.node.defaultChild).toBe(defaultChild);
486486
});
487487
test('returns "undefined" if there is no default', () => {
488-
const root = new Root();
488+
const root = Construct.createRoot();
489489
new Construct(root, 'child1');
490490
new Construct(root, 'child2');
491491

492492
expect(root.node.defaultChild).toBeUndefined();
493493
});
494494
test('fails if there are both "Resource" and "Default"', () => {
495-
const root = new Root();
495+
const root = Construct.createRoot();
496496
new Construct(root, 'child1');
497497
new Construct(root, 'Default');
498498
new Construct(root, 'child2');
@@ -507,7 +507,7 @@ describe('dependencies', () => {
507507

508508
test('addDependency() defines a dependency between two scopes', () => {
509509
// GIVEN
510-
const root = new Root();
510+
const root = Construct.createRoot();
511511
const consumer = new Construct(root, 'consumer');
512512
const producer1 = new Construct(root, 'producer1');
513513
const producer2 = new Construct(root, 'producer2');
@@ -522,7 +522,7 @@ describe('dependencies', () => {
522522

523523
test('are deduplicated', () => {
524524
// GIVEN
525-
const root = new Root();
525+
const root = Construct.createRoot();
526526
const consumer = new Construct(root, 'consumer');
527527
const producer = new Construct(root, 'producer');
528528

@@ -539,7 +539,7 @@ describe('dependencies', () => {
539539

540540
test('DependencyGroup can represent a group of disjoined producers', () => {
541541
// GIVEN
542-
const root = new Root();
542+
const root = Construct.createRoot();
543543
const group = new DependencyGroup(new Construct(root, 'producer1'), new Construct(root, 'producer2'));
544544
const consumer = new Construct(root, 'consumer');
545545

@@ -553,7 +553,7 @@ describe('dependencies', () => {
553553

554554
test('Dependable.implement() can be used to implement IDependable on any object', () => {
555555
// GIVEN
556-
const root = new Root();
556+
const root = Construct.createRoot();
557557
const producer = new Construct(root, 'producer');
558558
const consumer = new Construct(root, 'consumer');
559559

@@ -575,7 +575,7 @@ describe('dependencies', () => {
575575

576576
test('dependencyRoots are only resolved when node dependencies are evaluated', () => {
577577
// GIVEN
578-
const root = new Root();
578+
const root = Construct.createRoot();
579579
const c1 = new Construct(root, 'c1');
580580
const c2 = new Construct(root, 'c2');
581581
const c3 = new Construct(root, 'c3');
@@ -594,7 +594,7 @@ describe('dependencies', () => {
594594

595595
test('DependencyGroup can also include other IDependables', () => {
596596
// GIVEN
597-
const root = new Root();
597+
const root = Construct.createRoot();
598598
const c1 = new Construct(root, 'c1');
599599

600600
// WHEN
@@ -614,7 +614,7 @@ describe('dependencies', () => {
614614

615615
test('tryRemoveChild()', () => {
616616
// GIVEN
617-
const root = new Root();
617+
const root = Construct.createRoot();
618618
new Construct(root, 'child1');
619619
new Construct(root, 'child2');
620620

@@ -629,7 +629,7 @@ test('tryRemoveChild()', () => {
629629

630630
test('toString()', () => {
631631
// GIVEN
632-
const root = new Root();
632+
const root = Construct.createRoot();
633633
const child = new Construct(root, 'child');
634634
const grand = new Construct(child, 'grand');
635635

@@ -641,7 +641,7 @@ test('toString()', () => {
641641

642642
test('Construct.isConstruct returns true for constructs', () => {
643643
// GIVEN
644-
const root = new Root();
644+
const root = Construct.createRoot();
645645
class Subclass extends Construct {};
646646
const subclass = new Subclass(root, 'subclass');
647647
const someRandomObject = {};
@@ -659,7 +659,7 @@ test('Construct.isConstruct returns true for constructs', () => {
659659
});
660660

661661
function createTree(context?: any) {
662-
const root = new Root();
662+
const root = Construct.createRoot();
663663
const highChild = new Construct(root, 'HighChild');
664664
if (context) {
665665
Object.keys(context).forEach(key => highChild.node.setContext(key, context[key]));

0 commit comments

Comments
 (0)
Failed to load comments.