-
Notifications
You must be signed in to change notification settings - Fork 26
/
resolve.ts
150 lines (120 loc) · 3.52 KB
/
resolve.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { ApiObject } from './api-object';
import { App } from './app';
import { Lazy } from './lazy';
/**
* Context object for a specific resolution process.
*/
export class ResolutionContext {
/**
* The replaced value that was set via the `replaceValue` method.
*/
public replacedValue: any;
/**
* Whether or not the value was replaced by invoking the `replaceValue` method.
*/
public replaced: boolean = false;
constructor(
/**
* Which ApiObject is currently being resolved.
*/
public readonly obj: ApiObject,
/**
* Which key is currently being resolved.
*/
public readonly key: string[],
/**
* The value associated to the key currently being resolved.
*/
public readonly value: any) {
}
/**
* Replaces the original value in this resolution context
* with a new value. The new value is what will end up in the manifest.
*/
public replaceValue(newValue: any) {
this.replacedValue = newValue;
this.replaced = true;
}
}
/**
* Contract for resolver objects.
*/
export interface IResolver {
/**
* This function is invoked on every property during cdk8s synthesis.
* To replace a value, implementations must invoke `context.replaceValue`.
*/
resolve(context: ResolutionContext): void;
}
/**
* Resolvers instanecs of `Lazy`.
*/
export class LazyResolver implements IResolver {
public resolve(context: ResolutionContext): void {
if (context.value instanceof Lazy) {
const resolved = context.value.produce();
context.replaceValue(resolved);
}
}
}
/**
* Resolves implicit tokens.
*/
export class ImplicitTokenResolver implements IResolver {
public resolve(context: ResolutionContext): void {
if (typeof (context.value.resolve) === 'function') {
const resolved = context.value.resolve();
context.replaceValue(resolved);
}
}
}
/**
* Resolves union types that allow using either number or string (as generated by the CLI).
*
* E.g IntOrString, Quantity, ...
*/
export class NumberStringUnionResolver implements IResolver {
private static readonly TYPES = ['number', 'string'];
public resolve(context: ResolutionContext): void {
if (context.value.constructor === Object) {
// we only want to resolve union classes, not plain dictionaries
return;
}
if (NumberStringUnionResolver.TYPES.includes(typeof(context.value.value))) {
// replace with a dictionary because the L1 proceeds to access the .value
// property after resolution.
context.replaceValue({ value: context.value.value });
}
}
}
/**
* Resolves any value attached to a specific ApiObject.
*/
export function resolve(key: string[], value: any, apiObject: ApiObject): any {
const resolvers = App.of(apiObject).resolvers;
if (value == null) {
return value;
}
// give dibs to the resolvers as they are more specific
const context = new ResolutionContext(apiObject, key, value);
for (const resolver of resolvers) {
resolver.resolve(context);
if (context.replaced) {
// stop when the first resolver replaces the value.
return resolve(key, context.replacedValue, apiObject);
}
}
// array - resolve each element
if (Array.isArray(value)) {
return value.map((x, i) => resolve([...key, `${i}`], x, apiObject));
}
// dictionrary - resolve leafs
if (value.constructor == Object) {
const result: any = {};
for (const [k, v] of Object.entries(value)) {
result[k] = resolve([...key, k], v, apiObject);
}
return result;
}
return value;
}