@platforma-sdk/workflow-tengo 2.9.6 → 2.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tengo/lib/assets.lib.tengo +52 -52
- package/dist/tengo/lib/assets.swinfo.lib.tengo +45 -45
- package/dist/tengo/lib/constants.lib.tengo +32 -32
- package/dist/tengo/lib/exec.R.internal.lib.tengo +29 -29
- package/dist/tengo/lib/exec.R.lib.tengo +166 -166
- package/dist/tengo/lib/exec.constants.lib.tengo +4 -4
- package/dist/tengo/lib/exec.descriptor.lib.tengo +31 -31
- package/dist/tengo/lib/exec.internal.lib.tengo +297 -297
- package/dist/tengo/lib/exec.lib.tengo +815 -815
- package/dist/tengo/lib/exec.monetization.lib.tengo +130 -130
- package/dist/tengo/lib/exec.package.lib.tengo +212 -212
- package/dist/tengo/lib/exec.python.internal.lib.tengo +6 -6
- package/dist/tengo/lib/exec.python.lib.tengo +146 -146
- package/dist/tengo/lib/exec.runcmd.lib.tengo +371 -371
- package/dist/tengo/lib/exec.runenv.lib.tengo +92 -92
- package/dist/tengo/lib/exec.stream.lib.tengo +40 -40
- package/dist/tengo/lib/file.internal.lib.tengo +77 -77
- package/dist/tengo/lib/file.lib.tengo +57 -57
- package/dist/tengo/lib/ll.lib.tengo +244 -244
- package/dist/tengo/lib/maps.lib.tengo +152 -152
- package/dist/tengo/lib/objects.lib.tengo +13 -13
- package/dist/tengo/lib/oop.lib.tengo +19 -19
- package/dist/tengo/lib/path.lib.tengo +125 -125
- package/dist/tengo/lib/pframes.constants.lib.tengo +28 -28
- package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +33 -33
- package/dist/tengo/lib/pframes.lib.tengo +43 -43
- package/dist/tengo/lib/pframes.ll.lib.tengo +72 -72
- package/dist/tengo/lib/pframes.pcolumn.lib.tengo +49 -49
- package/dist/tengo/lib/pframes.spec.lib.tengo +96 -96
- package/dist/tengo/lib/pframes.util.lib.tengo +288 -288
- package/dist/tengo/lib/pframes.xsv.lib.tengo +272 -272
- package/dist/tengo/lib/render.lib.tengo +75 -75
- package/dist/tengo/lib/sets.lib.tengo +68 -68
- package/dist/tengo/lib/slices.lib.tengo +258 -155
- package/dist/tengo/lib/smart.lib.tengo +1069 -1069
- package/dist/tengo/lib/test.lib.tengo +161 -161
- package/dist/tengo/lib/tpl.lib.tengo +515 -515
- package/dist/tengo/lib/tpl.state-await.lib.tengo +240 -240
- package/dist/tengo/lib/tpl.util.lib.tengo +9 -9
- package/dist/tengo/lib/validation.lib.tengo +378 -378
- package/dist/tengo/lib/workdir.lib.tengo +331 -331
- package/dist/tengo/lib/workdir.proc.lib.tengo +144 -144
- package/dist/tengo/lib/workdir.save.lib.tengo +130 -130
- package/dist/tengo/lib/workflow.bobject.lib.tengo +26 -26
- package/dist/tengo/lib/workflow.bquery.lib.tengo +165 -165
- package/dist/tengo/lib/workflow.constants.lib.tengo +2 -2
- package/dist/tengo/lib/workflow.context.lib.tengo +32 -32
- package/dist/tengo/lib/workflow.lib.tengo +206 -206
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/* A library exposing template functional components */
|
|
2
2
|
|
|
3
3
|
tx := import("tx")
|
|
4
4
|
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
@@ -13,523 +13,523 @@ validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
|
13
13
|
|
|
14
14
|
bquery := import("@platforma-sdk/workflow-tengo:workflow.bquery")
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Unmarshal resource according by:
|
|
18
|
+
* - converting all json resources (potentially nested) into a tengo objects (maps)
|
|
19
|
+
* - converting all pl.maps (resource maps) into tengo maps
|
|
20
|
+
*
|
|
21
|
+
* @param resource: smart resource to unmarshal
|
|
22
|
+
* @param preprocessors: input preprocessors
|
|
23
|
+
* @return object: unmarshaled resource
|
|
24
|
+
*/
|
|
25
25
|
_unmarshal := func(resource, preprocessors) {
|
|
26
|
-
ll.assert(smart.isResource(resource), "expected resource, found ", resource)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self := func(resource) { return _unmarshal(resource, preprocessors) }
|
|
30
|
-
|
|
31
|
-
for preproc in preprocessors {
|
|
32
|
-
if preproc.canParse(resource) {
|
|
33
|
-
return preproc.parse(resource, self)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if smart.isReference(resource) && resource.hasError() {
|
|
38
|
-
ll.panic("input resource has errors: ", resource, resource.getError())
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if smart.isJsonResource(resource) || resource.checkResourceType(constants.RTYPE_BOBJECT_SPEC) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return resource.getDataAsJson()
|
|
45
|
-
} else if smart.isMapResource(resource) || smart.isLockedEphMapResource(resource) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
result := {}
|
|
49
|
-
for field, value in resource.inputs() {
|
|
50
|
-
if value.isErrorSet() {
|
|
51
|
-
ll.panic("input %v field has error: %v", field, field.getError().getDataAsJson().message)
|
|
52
|
-
} else if value.isSet() {
|
|
53
|
-
result[field] = _unmarshal(value.getValue(), preprocessors)
|
|
54
|
-
} else {
|
|
55
|
-
result[field] = value
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return result
|
|
60
|
-
} else {
|
|
61
|
-
|
|
62
|
-
return resource
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
26
|
+
ll.assert(smart.isResource(resource), "expected resource, found ", resource)
|
|
27
|
+
|
|
28
|
+
// instance of this
|
|
29
|
+
self := func(resource) { return _unmarshal(resource, preprocessors) }
|
|
30
|
+
// try preprocessors first
|
|
31
|
+
for preproc in preprocessors {
|
|
32
|
+
if preproc.canParse(resource) {
|
|
33
|
+
return preproc.parse(resource, self)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if smart.isReference(resource) && resource.hasError() {
|
|
38
|
+
ll.panic("input resource has errors: ", resource, resource.getError())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if smart.isJsonResource(resource) || resource.checkResourceType(constants.RTYPE_BOBJECT_SPEC) {
|
|
42
|
+
|
|
43
|
+
// parse json data of the resource
|
|
44
|
+
return resource.getDataAsJson()
|
|
45
|
+
} else if smart.isMapResource(resource) || smart.isLockedEphMapResource(resource) {
|
|
46
|
+
|
|
47
|
+
// unmarshal map
|
|
48
|
+
result := {}
|
|
49
|
+
for field, value in resource.inputs() {
|
|
50
|
+
if value.isErrorSet() {
|
|
51
|
+
ll.panic("input %v field has error: %v", field, field.getError().getDataAsJson().message)
|
|
52
|
+
} else if value.isSet() {
|
|
53
|
+
result[field] = _unmarshal(value.getValue(), preprocessors)
|
|
54
|
+
} else {
|
|
55
|
+
result[field] = value
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
return result
|
|
60
|
+
} else {
|
|
61
|
+
// nothing todo
|
|
62
|
+
return resource
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A wrapper object around template functionality
|
|
68
|
+
*/
|
|
69
69
|
tpl := func() {
|
|
70
|
-
self := undefined
|
|
71
|
-
|
|
72
|
-
_renderer := smart.resourceBuilder(ll.getCurrentTemplateRenderer())
|
|
73
|
-
|
|
74
|
-
_rendererType := _renderer.info().Type
|
|
75
|
-
|
|
76
|
-
_isEphemeral := _rendererType.Name == constants.RTYPE_RENDER_EPH_TEMPLATE.Name
|
|
77
|
-
|
|
78
|
-
_hasPrepared := _renderer.get(tplutil.INPUTS_MAP_FIELD).hasField(tplutil.PREPARE_MAP_FIELD_NAME)
|
|
79
|
-
|
|
80
|
-
_preparedMap := _hasPrepared ? _renderer.get(tplutil.INPUTS_MAP_FIELD).get(tplutil.PREPARE_MAP_FIELD_NAME) : undefined
|
|
81
|
-
|
|
82
|
-
_templateId := _renderer.get(tplutil.TEMPLATE_FIELD).id
|
|
83
|
-
|
|
84
|
-
_stateRequests := []
|
|
85
|
-
_delegatedOutputs := []
|
|
86
|
-
|
|
87
|
-
self = {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
renderer: func() {
|
|
93
|
-
return _renderer
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
isEphemeral: func() {
|
|
100
|
-
return _isEphemeral
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
template: func() {
|
|
107
|
-
return smart.resource(_templateId)
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
_unmarshallers: [bquery.resultUnmarshaller()],
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
registerUnmarshaller: func(preproc) {
|
|
118
|
-
ll.assert(is_undefined(self._inputs), "input preprocessors must be registered before getting inputs")
|
|
119
|
-
self._unmarshallers = append(self._unmarshallers, preproc)
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
rawInputs: func() {
|
|
126
|
-
return self.renderer().get(tplutil.INPUTS_MAP_FIELD).inputs()
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
_inputs: undefined,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
inputs: func() {
|
|
135
|
-
if self._inputs == undefined {
|
|
136
|
-
inputs := _unmarshal(self.renderer().get(tplutil.INPUTS_MAP_FIELD), self._unmarshallers)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
pp := inputs[tplutil.PREPARE_MAP_FIELD_NAME]
|
|
140
|
-
if !is_undefined(pp) {
|
|
141
|
-
delete(inputs, tplutil.PREPARE_MAP_FIELD_NAME)
|
|
142
|
-
for k, v in pp {
|
|
143
|
-
ll.assert(is_undefined(inputs[k]), k, " key already exists in inputs map")
|
|
144
|
-
inputs[k] = v
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
self._inputs = inputs
|
|
149
|
-
}
|
|
150
|
-
return self._inputs
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
hasInput: func(name) {
|
|
157
|
-
return self.renderer().get(tplutil.INPUTS_MAP_FIELD).hasField(name)
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
definedOutputs : [],
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
defineOutputs : func(...name) {
|
|
170
|
-
if len(name) == 1 && is_array(name[0]) {
|
|
171
|
-
for v in name[0] {
|
|
172
|
-
self.definedOutputs = append(self.definedOutputs, v)
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
self.definedOutputs = append(self.definedOutputs, name...)
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
awaitState: func(...pathAndState) {
|
|
213
|
-
if !self.isEphemeral() {
|
|
214
|
-
ll.panic("awaitState allowed only in ephemeral templates")
|
|
215
|
-
}
|
|
216
|
-
path := pathAndState[:len(pathAndState) - 1]
|
|
217
|
-
state := pathAndState[len(pathAndState) - 1]
|
|
218
|
-
|
|
219
|
-
_stateRequests = append(_stateRequests, {
|
|
220
|
-
path: path,
|
|
221
|
-
state: state
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
ignorePrepare: _hasPrepared,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
preparedMapResource: func() {
|
|
243
|
-
return _preparedMap
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
prepareQueue : [],
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
prepare : func(codeFn) {
|
|
258
|
-
if self.ignorePrepare {
|
|
259
|
-
return
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
ll.assert(len(self.prepareQueue) == 0, "multiple prepare statements are not supported yet")
|
|
263
|
-
|
|
264
|
-
self.prepareQueue = append(self.prepareQueue, codeFn)
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
renderPrepare: func() {
|
|
272
|
-
ll.assert(!self.ignorePrepare, "expected !ignorePrepare")
|
|
273
|
-
|
|
274
|
-
inputs := self.inputs()
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
newInputs := copy(inputs)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
builder := undefined
|
|
281
|
-
if self.isEphemeral() {
|
|
282
|
-
builder = smart.ephemeralMapBuilder()
|
|
283
|
-
} else {
|
|
284
|
-
builder = smart.mapBuilder()
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
self._validateInputs()
|
|
289
|
-
for cb in self.prepareQueue {
|
|
290
|
-
|
|
291
|
-
r := cb(inputs)
|
|
292
|
-
|
|
293
|
-
ll.assert(is_map(r), "expected map, got ", r)
|
|
294
|
-
|
|
295
|
-
for name, ref in r {
|
|
296
|
-
ll.assert(is_undefined(newInputs[name]), "prepare name is already in use: %s", name)
|
|
297
|
-
|
|
298
|
-
builder.add(name, ref)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
newInputs[tplutil.PREPARE_MAP_FIELD_NAME] = builder.build()
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
renderer := undefined
|
|
305
|
-
if self.isEphemeral() {
|
|
306
|
-
renderer = render.createEphemeral(self.template(), newInputs)
|
|
307
|
-
} else {
|
|
308
|
-
renderer = render.create(self.template(), newInputs)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
for output in self.definedOutputs {
|
|
312
|
-
self.getOutputField(output).set(renderer.output(output))
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
setOutputs : func(resultMap) {
|
|
321
|
-
ll.assert(ll.isMap(resultMap), "expected map, got ", resultMap)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for name, value in resultMap {
|
|
325
|
-
f := self.getOutputField(name)
|
|
326
|
-
if !f.isSet() {
|
|
327
|
-
f.setRefOrJson(value)
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
getOutputField: func(name) {
|
|
336
|
-
return self.renderer().getField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
createOutputsAndLock: func(outputs) {
|
|
343
|
-
for name in outputs {
|
|
344
|
-
self.renderer().createOutputField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
|
|
345
|
-
}
|
|
346
|
-
self.renderer().lockOutputs()
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
_inputsSchema: undefined,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
validateInputs: func(schema) {
|
|
355
|
-
ll.assert(is_undefined(self._inputsSchema), "validateInputs was already invoked")
|
|
356
|
-
self._inputsSchema = schema
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
_validateInputs: func() {
|
|
360
|
-
schema := self._inputsSchema
|
|
361
|
-
if is_undefined(schema) {
|
|
362
|
-
return
|
|
363
|
-
}
|
|
364
|
-
validation.assertJsonSchema(self.inputs(), schema)
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
body: func(bodyFn){
|
|
371
|
-
if self.isEphemeral() {
|
|
372
|
-
|
|
373
|
-
self.routeEph(bodyFn)
|
|
374
|
-
} else {
|
|
375
|
-
|
|
376
|
-
self.routePure(bodyFn)
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
routePure: func(bodyFn){
|
|
384
|
-
|
|
385
|
-
if len(self.definedOutputs) == 0 {
|
|
386
|
-
ll.panic("please define outputs before template body")
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if ll.isInitializing() {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
self.createOutputsAndLock(self.definedOutputs)
|
|
405
|
-
} else {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if len(self.prepareQueue) != 0 {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
self.renderPrepare()
|
|
412
|
-
} else {
|
|
413
|
-
|
|
414
|
-
self._validateInputs()
|
|
415
|
-
result := bodyFn(self.inputs())
|
|
416
|
-
|
|
417
|
-
ll.assert(ll.isMap(result),
|
|
418
|
-
"expected map from tpl.body() callback. Did you forget 'return' statement?")
|
|
419
|
-
|
|
420
|
-
outputsWithValues := sets.fromMapKeys(result)
|
|
421
|
-
outputsWithValues = sets.add(outputsWithValues, _delegatedOutputs...)
|
|
422
|
-
|
|
423
|
-
ll.assert(sets.fromSlice(self.definedOutputs) == outputsWithValues,
|
|
424
|
-
"tpl.body() callback must return the same list of outputs as were given to 'defineOutputs()'. Want %s, got %s",
|
|
425
|
-
self.definedOutputs, sets.toSlice(outputsWithValues))
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
self.setOutputs(result)
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
routeEph: func(bodyFn) {
|
|
437
|
-
|
|
438
|
-
if len(_stateRequests) == 0 {
|
|
439
|
-
_stateRequests = [{
|
|
440
|
-
path: [],
|
|
441
|
-
state: "AllInputsSet"
|
|
442
|
-
}]
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if _hasPrepared {
|
|
446
|
-
_stateRequests = append(_stateRequests, {
|
|
447
|
-
path: [tplutil.PREPARE_MAP_FIELD_NAME],
|
|
448
|
-
state: "ResourceReady"
|
|
449
|
-
})
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
stateReached := stateAwait.await(
|
|
453
|
-
_renderer.getField(tplutil.INPUTS_MAP_FIELD),
|
|
454
|
-
_renderer,
|
|
455
|
-
_stateRequests
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
if stateReached {
|
|
459
|
-
|
|
460
|
-
if len(self.prepareQueue) > 0 {
|
|
461
|
-
ll.assert(len(self.definedOutputs) > 0, "outputs should be defined prior to prepare")
|
|
462
|
-
|
|
463
|
-
self.createOutputsAndLock(self.definedOutputs)
|
|
464
|
-
|
|
465
|
-
self.renderPrepare()
|
|
466
|
-
} else {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
self._validateInputs()
|
|
470
|
-
result := bodyFn(self.inputs())
|
|
471
|
-
ll.assert(ll.isMap(result),
|
|
472
|
-
"expected map from tpl-eph.body() callback. Did you forget 'return' statement?")
|
|
473
|
-
|
|
474
|
-
self.createOutputsAndLock(maps.getKeys(result))
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
self.setOutputs(result)
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
routeDelegate: func(tpl, extraInputs, outputs) {
|
|
491
|
-
_delegatedOutputs = outputs
|
|
492
|
-
|
|
493
|
-
inputs := self.rawInputs()
|
|
494
|
-
for k, v in extraInputs {
|
|
495
|
-
inputs[k] = v
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
renderer := undefined
|
|
499
|
-
if self.isEphemeral() {
|
|
500
|
-
renderer = render.createEphemeral(tpl, inputs)
|
|
501
|
-
} else {
|
|
502
|
-
renderer = render.create(tpl, inputs)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
tplOutputs := {}
|
|
506
|
-
for o in outputs {
|
|
507
|
-
tplOutputs[o] = renderer.output(o)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
self.createOutputsAndLock(outputs)
|
|
511
|
-
self.setOutputs(tplOutputs)
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return ll.toStrict(self)
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
70
|
+
self := undefined
|
|
71
|
+
|
|
72
|
+
_renderer := smart.resourceBuilder(ll.getCurrentTemplateRenderer())
|
|
73
|
+
|
|
74
|
+
_rendererType := _renderer.info().Type
|
|
75
|
+
|
|
76
|
+
_isEphemeral := _rendererType.Name == constants.RTYPE_RENDER_EPH_TEMPLATE.Name
|
|
77
|
+
|
|
78
|
+
_hasPrepared := _renderer.get(tplutil.INPUTS_MAP_FIELD).hasField(tplutil.PREPARE_MAP_FIELD_NAME)
|
|
79
|
+
|
|
80
|
+
_preparedMap := _hasPrepared ? _renderer.get(tplutil.INPUTS_MAP_FIELD).get(tplutil.PREPARE_MAP_FIELD_NAME) : undefined
|
|
81
|
+
|
|
82
|
+
_templateId := _renderer.get(tplutil.TEMPLATE_FIELD).id
|
|
83
|
+
|
|
84
|
+
_stateRequests := []
|
|
85
|
+
_delegatedOutputs := []
|
|
86
|
+
|
|
87
|
+
self = {
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Current renderer smart resource
|
|
91
|
+
*/
|
|
92
|
+
renderer: func() {
|
|
93
|
+
return _renderer
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether this template is ephemeral (rendered by ephemeral template router)
|
|
98
|
+
*/
|
|
99
|
+
isEphemeral: func() {
|
|
100
|
+
return _isEphemeral
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Current template resource
|
|
105
|
+
*/
|
|
106
|
+
template: func() {
|
|
107
|
+
return smart.resource(_templateId)
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
_unmarshallers: [bquery.resultUnmarshaller()],
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Register a preprocessor of input resource. The preprocessor must have two
|
|
114
|
+
* methods: `canParse(resource)` (bool) and `parse(resource)` (object). Registered
|
|
115
|
+
* preprocessors will be used when unmarshaling resources.
|
|
116
|
+
*/
|
|
117
|
+
registerUnmarshaller: func(preproc) {
|
|
118
|
+
ll.assert(is_undefined(self._inputs), "input preprocessors must be registered before getting inputs")
|
|
119
|
+
self._unmarshallers = append(self._unmarshallers, preproc)
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @return map[string]field: - a map of inputs to the fields of the input map.
|
|
124
|
+
*/
|
|
125
|
+
rawInputs: func() {
|
|
126
|
+
return self.renderer().get(tplutil.INPUTS_MAP_FIELD).inputs()
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
_inputs: undefined,
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @return map: a map of the template inputs
|
|
133
|
+
*/
|
|
134
|
+
inputs: func() {
|
|
135
|
+
if self._inputs == undefined {
|
|
136
|
+
inputs := _unmarshal(self.renderer().get(tplutil.INPUTS_MAP_FIELD), self._unmarshallers)
|
|
137
|
+
|
|
138
|
+
// if we have prepared inputs, flatten them
|
|
139
|
+
pp := inputs[tplutil.PREPARE_MAP_FIELD_NAME]
|
|
140
|
+
if !is_undefined(pp) {
|
|
141
|
+
delete(inputs, tplutil.PREPARE_MAP_FIELD_NAME)
|
|
142
|
+
for k, v in pp {
|
|
143
|
+
ll.assert(is_undefined(inputs[k]), k, " key already exists in inputs map")
|
|
144
|
+
inputs[k] = v
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
self._inputs = inputs
|
|
149
|
+
}
|
|
150
|
+
return self._inputs
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @return bool: whether there is input with a given name
|
|
155
|
+
*/
|
|
156
|
+
hasInput: func(name) {
|
|
157
|
+
return self.renderer().get(tplutil.INPUTS_MAP_FIELD).hasField(name)
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
// accumulated output definitions
|
|
161
|
+
definedOutputs : [],
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Defines current template output(s) names.
|
|
165
|
+
*
|
|
166
|
+
* For pure templates, all outputs must be defined before the template body, and only registered outputs can be used
|
|
167
|
+
* inside. Optional for ephemeral templates.
|
|
168
|
+
*/
|
|
169
|
+
defineOutputs : func(...name) {
|
|
170
|
+
if len(name) == 1 && is_array(name[0]) {
|
|
171
|
+
for v in name[0] {
|
|
172
|
+
self.definedOutputs = append(self.definedOutputs, v)
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
self.definedOutputs = append(self.definedOutputs, name...)
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* In ephemeral template, body will only be executed when corresponding input staet is
|
|
181
|
+
* reached.
|
|
182
|
+
*
|
|
183
|
+
* Signature:
|
|
184
|
+
* awaitState(path..., requestedState)
|
|
185
|
+
*
|
|
186
|
+
* Usage examples:
|
|
187
|
+
* awaitState("AllInputsSet") - all inputs of the inputs map must be set
|
|
188
|
+
* awaitState("inputName1", "ResourceReady") - resource in the input inputName1 must be ready
|
|
189
|
+
* awaitState("inputName1", "nestedField", "Set") - nested input field in the input
|
|
190
|
+
* inputName1 must exist and point to some resource
|
|
191
|
+
* awaitState("inputName1", { wildcard: "*" }, "InputsLocked") - nested resource by the
|
|
192
|
+
* inputName1 field must exist and have locked inputs and all
|
|
193
|
+
* resources referenced by those fields must have all inputs
|
|
194
|
+
* locked
|
|
195
|
+
* awaitState("inputName1", { match: "^some_prefix\\." }, "InputsLocked") - nested resource
|
|
196
|
+
* by the inputName1 field must exist and have locked inputs and
|
|
197
|
+
* all resources referenced by inputs with "some_prefix." prefix
|
|
198
|
+
* must have all inputs locked
|
|
199
|
+
*
|
|
200
|
+
* Example, the following two expressions are equivalent:
|
|
201
|
+
* awaitState("inputName1", { wildcard: "*" }, "Set")
|
|
202
|
+
* awaitState("inputName1", { match: "^some_prefix\\." }, "Set")
|
|
203
|
+
* awaitState("inputName1", "AllInputsSet")
|
|
204
|
+
*
|
|
205
|
+
* Allowed states:
|
|
206
|
+
* Exists - field exists
|
|
207
|
+
* Set - field points to existing resource
|
|
208
|
+
* InputsLocked - field points to resource with locked inputs
|
|
209
|
+
* AllInputsSet - field points to resource with all inputs set
|
|
210
|
+
* ResourceReady - field points to ready resource
|
|
211
|
+
*/
|
|
212
|
+
awaitState: func(...pathAndState) {
|
|
213
|
+
if !self.isEphemeral() {
|
|
214
|
+
ll.panic("awaitState allowed only in ephemeral templates")
|
|
215
|
+
}
|
|
216
|
+
path := pathAndState[:len(pathAndState) - 1]
|
|
217
|
+
state := pathAndState[len(pathAndState) - 1]
|
|
218
|
+
// if len(self.prepareQueue) == 0 {
|
|
219
|
+
_stateRequests = append(_stateRequests, {
|
|
220
|
+
path: path,
|
|
221
|
+
state: state
|
|
222
|
+
})
|
|
223
|
+
// } else if len(self.prepareQueue) == 1 {
|
|
224
|
+
// _stateRequestsAfterPrepare = append(_stateRequestsAfterPrepare, {
|
|
225
|
+
// path: path,
|
|
226
|
+
// state: state
|
|
227
|
+
// })
|
|
228
|
+
// } else {
|
|
229
|
+
// ll.panic("Multiple prepare are not supported")
|
|
230
|
+
// }
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* True means we are in the inner render cycle, when all "to be prepared" fields are passed
|
|
235
|
+
* already to inputs and no need to call prepare callbacks again.
|
|
236
|
+
*/
|
|
237
|
+
ignorePrepare: _hasPrepared,
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Resource map with the resources passed
|
|
241
|
+
*/
|
|
242
|
+
preparedMapResource: func() {
|
|
243
|
+
return _preparedMap
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* A queue of 'prepare' callbacks
|
|
248
|
+
*/
|
|
249
|
+
prepareQueue : [],
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Prepare additional resources for the template body. The body function of the workflow will be executed only when the references
|
|
253
|
+
* returned from prepare method will be resolved into a ready resources. The resulting resources will be passed as an input to the body function.
|
|
254
|
+
*
|
|
255
|
+
* @param cb: a callback function returning a map of references to be resolved
|
|
256
|
+
*/
|
|
257
|
+
prepare : func(codeFn) {
|
|
258
|
+
if self.ignorePrepare {
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
ll.assert(len(self.prepareQueue) == 0, "multiple prepare statements are not supported yet")
|
|
263
|
+
|
|
264
|
+
self.prepareQueue = append(self.prepareQueue, codeFn)
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Creates renderer which is re-rendering the current template additionally passing
|
|
269
|
+
* resolve references as extra inputs.
|
|
270
|
+
*/
|
|
271
|
+
renderPrepare: func() {
|
|
272
|
+
ll.assert(!self.ignorePrepare, "expected !ignorePrepare")
|
|
273
|
+
|
|
274
|
+
inputs := self.inputs()
|
|
275
|
+
|
|
276
|
+
// create new inputs map
|
|
277
|
+
newInputs := copy(inputs)
|
|
278
|
+
|
|
279
|
+
// build a prepare map
|
|
280
|
+
builder := undefined
|
|
281
|
+
if self.isEphemeral() {
|
|
282
|
+
builder = smart.ephemeralMapBuilder()
|
|
283
|
+
} else {
|
|
284
|
+
builder = smart.mapBuilder()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// validate inputs first if needed
|
|
288
|
+
self._validateInputs()
|
|
289
|
+
for cb in self.prepareQueue {
|
|
290
|
+
// run callback
|
|
291
|
+
r := cb(inputs)
|
|
292
|
+
|
|
293
|
+
ll.assert(is_map(r), "expected map, got ", r)
|
|
294
|
+
|
|
295
|
+
for name, ref in r {
|
|
296
|
+
ll.assert(is_undefined(newInputs[name]), "prepare name is already in use: %s", name)
|
|
297
|
+
|
|
298
|
+
builder.add(name, ref)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
newInputs[tplutil.PREPARE_MAP_FIELD_NAME] = builder.build()
|
|
302
|
+
|
|
303
|
+
// re-render self
|
|
304
|
+
renderer := undefined
|
|
305
|
+
if self.isEphemeral() {
|
|
306
|
+
renderer = render.createEphemeral(self.template(), newInputs)
|
|
307
|
+
} else {
|
|
308
|
+
renderer = render.create(self.template(), newInputs)
|
|
309
|
+
}
|
|
310
|
+
// set outputs
|
|
311
|
+
for output in self.definedOutputs {
|
|
312
|
+
self.getOutputField(output).set(renderer.output(output))
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Sets results map to resource output fields. Non-resource objects in the result values
|
|
318
|
+
* will be automatically converted into Json resources.
|
|
319
|
+
*/
|
|
320
|
+
setOutputs : func(resultMap) {
|
|
321
|
+
ll.assert(ll.isMap(resultMap), "expected map, got ", resultMap)
|
|
322
|
+
|
|
323
|
+
// set outputs
|
|
324
|
+
for name, value in resultMap {
|
|
325
|
+
f := self.getOutputField(name)
|
|
326
|
+
if !f.isSet() {
|
|
327
|
+
f.setRefOrJson(value)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Returns renderer's output field with a given name
|
|
334
|
+
*/
|
|
335
|
+
getOutputField: func(name) {
|
|
336
|
+
return self.renderer().getField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Creates relevant output fields and locks renderer's outputs
|
|
341
|
+
*/
|
|
342
|
+
createOutputsAndLock: func(outputs) {
|
|
343
|
+
for name in outputs {
|
|
344
|
+
self.renderer().createOutputField(tplutil.TEMPLATE_OUTPUT_PREFIX + name)
|
|
345
|
+
}
|
|
346
|
+
self.renderer().lockOutputs()
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
_inputsSchema: undefined,
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Validate inputs schema before passing inputs to body.
|
|
353
|
+
*/
|
|
354
|
+
validateInputs: func(schema) {
|
|
355
|
+
ll.assert(is_undefined(self._inputsSchema), "validateInputs was already invoked")
|
|
356
|
+
self._inputsSchema = schema
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
_validateInputs: func() {
|
|
360
|
+
schema := self._inputsSchema
|
|
361
|
+
if is_undefined(schema) {
|
|
362
|
+
return
|
|
363
|
+
}
|
|
364
|
+
validation.assertJsonSchema(self.inputs(), schema)
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Routes template.
|
|
369
|
+
*/
|
|
370
|
+
body: func(bodyFn){
|
|
371
|
+
if self.isEphemeral() {
|
|
372
|
+
|
|
373
|
+
self.routeEph(bodyFn)
|
|
374
|
+
} else {
|
|
375
|
+
|
|
376
|
+
self.routePure(bodyFn)
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Routes pure template.
|
|
382
|
+
*/
|
|
383
|
+
routePure: func(bodyFn){
|
|
384
|
+
// Checks
|
|
385
|
+
if len(self.definedOutputs) == 0 {
|
|
386
|
+
ll.panic("please define outputs before template body")
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//
|
|
390
|
+
// Rules of pl guarantees that pure template code will be executed twice:
|
|
391
|
+
//
|
|
392
|
+
// (1) once for initialization, at this point it must initialize all outputs, for the
|
|
393
|
+
// deduplication and recovery mechanisms to pick them up and, if possible, populate them
|
|
394
|
+
// with results from equivalent previous invocations
|
|
395
|
+
//
|
|
396
|
+
// (2) after initialization is done and all deduplication and recovery algorithms did their
|
|
397
|
+
// magic, if some (or all) of the outputs, our consumers demand are still absent, main
|
|
398
|
+
// body of the template is executed, to calculate them
|
|
399
|
+
//
|
|
400
|
+
|
|
401
|
+
if ll.isInitializing() {
|
|
402
|
+
|
|
403
|
+
// Initialization
|
|
404
|
+
self.createOutputsAndLock(self.definedOutputs)
|
|
405
|
+
} else {
|
|
406
|
+
|
|
407
|
+
// processing resolves
|
|
408
|
+
if len(self.prepareQueue) != 0 {
|
|
409
|
+
|
|
410
|
+
// re-render self to
|
|
411
|
+
self.renderPrepare()
|
|
412
|
+
} else {
|
|
413
|
+
// run body func
|
|
414
|
+
self._validateInputs()
|
|
415
|
+
result := bodyFn(self.inputs())
|
|
416
|
+
|
|
417
|
+
ll.assert(ll.isMap(result),
|
|
418
|
+
"expected map from tpl.body() callback. Did you forget 'return' statement?")
|
|
419
|
+
|
|
420
|
+
outputsWithValues := sets.fromMapKeys(result)
|
|
421
|
+
outputsWithValues = sets.add(outputsWithValues, _delegatedOutputs...)
|
|
422
|
+
|
|
423
|
+
ll.assert(sets.fromSlice(self.definedOutputs) == outputsWithValues,
|
|
424
|
+
"tpl.body() callback must return the same list of outputs as were given to 'defineOutputs()'. Want %s, got %s",
|
|
425
|
+
self.definedOutputs, sets.toSlice(outputsWithValues))
|
|
426
|
+
|
|
427
|
+
// set result to self outputs
|
|
428
|
+
self.setOutputs(result)
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Routes ephemeral template.
|
|
435
|
+
*/
|
|
436
|
+
routeEph: func(bodyFn) {
|
|
437
|
+
|
|
438
|
+
if len(_stateRequests) == 0 {
|
|
439
|
+
_stateRequests = [{
|
|
440
|
+
path: [],
|
|
441
|
+
state: "AllInputsSet"
|
|
442
|
+
}]
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if _hasPrepared {
|
|
446
|
+
_stateRequests = append(_stateRequests, {
|
|
447
|
+
path: [tplutil.PREPARE_MAP_FIELD_NAME],
|
|
448
|
+
state: "ResourceReady"
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
stateReached := stateAwait.await(
|
|
453
|
+
_renderer.getField(tplutil.INPUTS_MAP_FIELD),
|
|
454
|
+
_renderer,
|
|
455
|
+
_stateRequests
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
if stateReached {
|
|
459
|
+
|
|
460
|
+
if len(self.prepareQueue) > 0 {
|
|
461
|
+
ll.assert(len(self.definedOutputs) > 0, "outputs should be defined prior to prepare")
|
|
462
|
+
|
|
463
|
+
self.createOutputsAndLock(self.definedOutputs)
|
|
464
|
+
|
|
465
|
+
self.renderPrepare()
|
|
466
|
+
} else {
|
|
467
|
+
|
|
468
|
+
// run body func
|
|
469
|
+
self._validateInputs()
|
|
470
|
+
result := bodyFn(self.inputs())
|
|
471
|
+
ll.assert(ll.isMap(result),
|
|
472
|
+
"expected map from tpl-eph.body() callback. Did you forget 'return' statement?")
|
|
473
|
+
|
|
474
|
+
self.createOutputsAndLock(maps.getKeys(result))
|
|
475
|
+
|
|
476
|
+
// set result to self outputs
|
|
477
|
+
self.setOutputs(result)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Delegates itself to the specified template, i.e. runs specified template and
|
|
484
|
+
* saves its outputs to this.
|
|
485
|
+
*
|
|
486
|
+
* @param tpl: template - specified template
|
|
487
|
+
* @param extraInputs: map - additional inputs to pass to the template
|
|
488
|
+
* @param outputs: array - array of expected outputs to save
|
|
489
|
+
*/
|
|
490
|
+
routeDelegate: func(tpl, extraInputs, outputs) {
|
|
491
|
+
_delegatedOutputs = outputs
|
|
492
|
+
|
|
493
|
+
inputs := self.rawInputs()
|
|
494
|
+
for k, v in extraInputs {
|
|
495
|
+
inputs[k] = v
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
renderer := undefined
|
|
499
|
+
if self.isEphemeral() {
|
|
500
|
+
renderer = render.createEphemeral(tpl, inputs)
|
|
501
|
+
} else {
|
|
502
|
+
renderer = render.create(tpl, inputs)
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
tplOutputs := {}
|
|
506
|
+
for o in outputs {
|
|
507
|
+
tplOutputs[o] = renderer.output(o)
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
self.createOutputsAndLock(outputs)
|
|
511
|
+
self.setOutputs(tplOutputs)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return ll.toStrict(self)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Singleton instance of this resource
|
|
519
|
+
*/
|
|
520
520
|
instance := tpl()
|
|
521
521
|
|
|
522
522
|
export ll.toStrict({
|
|
523
|
-
renderer : instance.renderer,
|
|
524
|
-
registerUnmarshaller : instance.registerUnmarshaller,
|
|
525
|
-
rawInputs : instance.rawInputs,
|
|
526
|
-
hasInput : instance.hasInput,
|
|
527
|
-
inputs : instance.inputs,
|
|
528
|
-
defineOutputs : instance.defineOutputs,
|
|
529
|
-
prepare : instance.prepare,
|
|
530
|
-
awaitState : instance.awaitState,
|
|
531
|
-
delegate : instance.routeDelegate,
|
|
532
|
-
validateInputs : instance.validateInputs,
|
|
533
|
-
body : instance.body,
|
|
534
|
-
template : instance.template
|
|
523
|
+
renderer : instance.renderer,
|
|
524
|
+
registerUnmarshaller : instance.registerUnmarshaller,
|
|
525
|
+
rawInputs : instance.rawInputs,
|
|
526
|
+
hasInput : instance.hasInput,
|
|
527
|
+
inputs : instance.inputs,
|
|
528
|
+
defineOutputs : instance.defineOutputs,
|
|
529
|
+
prepare : instance.prepare,
|
|
530
|
+
awaitState : instance.awaitState,
|
|
531
|
+
delegate : instance.routeDelegate,
|
|
532
|
+
validateInputs : instance.validateInputs,
|
|
533
|
+
body : instance.body,
|
|
534
|
+
template : instance.template
|
|
535
535
|
})
|