@platforma-sdk/workflow-tengo 2.8.0 → 2.8.2

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