@platforma-sdk/workflow-tengo 4.6.5 → 4.7.1
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/exec.constants.lib.tengo +6 -2
- package/dist/tengo/lib/exec.lib.tengo +9 -6
- package/dist/tengo/lib/exec.limits.lib.tengo +38 -13
- package/dist/tengo/lib/exec.runcmd.lib.tengo +35 -23
- package/dist/tengo/lib/feats.lib.tengo +17 -1
- package/dist/tengo/lib/smart.lib.tengo +16 -0
- package/dist/tengo/lib/tpl.base.lib.tengo +6 -2
- package/dist/tengo/lib/tpl.state-await.lib.tengo +88 -29
- package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
- package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
- 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_info.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/exec.python.venv-bin-path.plj.gz +0 -0
- package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe-for-ui.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.process-pcolumn-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/waiter.waiter.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/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
- package/package.json +1 -1
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
|
|
1
3
|
ARG_TYPE_STRING := "string"
|
|
2
4
|
ARG_TYPE_EXPRESSION := "expressionRef"
|
|
3
5
|
ARG_TYPE_VAR := "variableRef" // TODO: deprecated, remove after 15 Mar 2025
|
|
4
6
|
ARG_TYPES := [ARG_TYPE_STRING, ARG_TYPE_EXPRESSION, ARG_TYPE_VAR]
|
|
5
7
|
|
|
8
|
+
BATCH_QUEUE := "batch" // special queue in storage controller with no limits
|
|
6
9
|
HEAVY_QUEUE := "heavy"
|
|
7
10
|
MEDIUM_QUEUE := "medium"
|
|
8
11
|
LIGHT_QUEUE := "light"
|
|
9
12
|
UI_TASKS_QUEUE := "ui-tasks"
|
|
10
13
|
|
|
11
|
-
export {
|
|
14
|
+
export ll.toStrict({
|
|
12
15
|
ARG_TYPE_STRING: ARG_TYPE_STRING,
|
|
13
16
|
ARG_TYPE_EXPRESSION: ARG_TYPE_EXPRESSION,
|
|
14
17
|
ARG_TYPE_VAR: ARG_TYPE_VAR,
|
|
15
18
|
ARG_TYPES: ARG_TYPES,
|
|
16
19
|
|
|
20
|
+
BATCH_QUEUE: BATCH_QUEUE,
|
|
17
21
|
UI_TASKS_QUEUE: UI_TASKS_QUEUE,
|
|
18
22
|
LIGHT_QUEUE: LIGHT_QUEUE,
|
|
19
23
|
MEDIUM_QUEUE: MEDIUM_QUEUE,
|
|
20
24
|
HEAVY_QUEUE: HEAVY_QUEUE
|
|
21
|
-
}
|
|
25
|
+
})
|
|
@@ -759,12 +759,15 @@ builder := func() {
|
|
|
759
759
|
}
|
|
760
760
|
|
|
761
761
|
serviceExecInputs := {}
|
|
762
|
-
if feats.workdirLimits || feats.computeLimits {
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
762
|
+
if feats.workdirLimits || feats.computeLimits || limits.useBatch(queue) {
|
|
763
|
+
if (is_int(ramRequest)) {
|
|
764
|
+
ramRequest = string(ramRequest) + "b"
|
|
765
|
+
}
|
|
766
|
+
serviceExecInputs.quota = {
|
|
767
|
+
queue: queue,
|
|
768
|
+
cpu: cpuRequest,
|
|
769
|
+
ram: ramRequest
|
|
770
|
+
}
|
|
768
771
|
}
|
|
769
772
|
|
|
770
773
|
|
|
@@ -9,8 +9,11 @@ ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
|
9
9
|
|
|
10
10
|
oop := import("@platforma-sdk/workflow-tengo:oop")
|
|
11
11
|
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
12
|
+
slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
12
13
|
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
13
14
|
json := import("json")
|
|
15
|
+
constants := import("@platforma-sdk/workflow-tengo:exec.constants")
|
|
16
|
+
feats := import("@platforma-sdk/workflow-tengo:feats")
|
|
14
17
|
|
|
15
18
|
_RTYPE_QUOTA := { Name: "Quota", Version: "1" }
|
|
16
19
|
|
|
@@ -19,6 +22,16 @@ _RTYPE_QUOTA := { Name: "Quota", Version: "1" }
|
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
|
|
25
|
+
|
|
26
|
+
useBatch := func(queueName) {
|
|
27
|
+
return feats.hasBatch && !slices.hasElement([ constants.UI_TASKS_QUEUE ], queueName)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
22
35
|
quotaBuilder := func() {
|
|
23
36
|
self := undefined
|
|
24
37
|
|
|
@@ -132,9 +145,11 @@ storageSpaceRequestBuilder := func() {
|
|
|
132
145
|
requestBuilder := smart.ephemeralBuilder(_RTYPE_STORAGE_SPACE_REQUEST)
|
|
133
146
|
requestBuilder.getField(_SSR_FIELD_REQUEST).set(quota)
|
|
134
147
|
|
|
148
|
+
requestRef := requestBuilder.lockAndBuild()
|
|
149
|
+
|
|
135
150
|
return ll.toStrict(
|
|
136
|
-
oop.inherit(
|
|
137
|
-
allocation:
|
|
151
|
+
oop.inherit(requestRef, {
|
|
152
|
+
allocation: requestRef.getField(_SSR_FIELD_ALLOCATION)
|
|
138
153
|
})
|
|
139
154
|
)
|
|
140
155
|
}
|
|
@@ -143,8 +158,9 @@ storageSpaceRequestBuilder := func() {
|
|
|
143
158
|
return self
|
|
144
159
|
}
|
|
145
160
|
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
_RTYPE_COMPUTE_REQUEST_EXECUTOR_V1 := { Name: "ComputeRequest/executor", Version: "1" }
|
|
162
|
+
_RTYPE_COMPUTE_REQUEST_BATCH_V1 := { Name: "ComputeRequest/batch", Version: "1" }
|
|
163
|
+
|
|
148
164
|
_CR_FIELD_REQUEST := "request" // input
|
|
149
165
|
_CR_FIELD_ALLOCATION := "allocation" // output
|
|
150
166
|
|
|
@@ -152,7 +168,7 @@ computeRequestBuilder := func() {
|
|
|
152
168
|
self := undefined
|
|
153
169
|
|
|
154
170
|
quota := undefined
|
|
155
|
-
|
|
171
|
+
queue_name := undefined
|
|
156
172
|
|
|
157
173
|
self = ll.toStrict({
|
|
158
174
|
|
|
@@ -173,10 +189,9 @@ computeRequestBuilder := func() {
|
|
|
173
189
|
|
|
174
190
|
|
|
175
191
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
executor = execID
|
|
192
|
+
queue: func(name) {
|
|
193
|
+
validation.assertType(name, "string", "exec.limits.queue: queue name should be a string")
|
|
194
|
+
queue_name = name
|
|
180
195
|
|
|
181
196
|
return self
|
|
182
197
|
},
|
|
@@ -188,14 +203,23 @@ computeRequestBuilder := func() {
|
|
|
188
203
|
|
|
189
204
|
build: func() {
|
|
190
205
|
ll.assert(smart.isReference(quota), "exec.limits.build: quota of ComputeRequest must be a valid reference")
|
|
206
|
+
ll.assert(queue_name != "", "exec.limits.build: queue name is required for computeRequestBuilder")
|
|
207
|
+
|
|
208
|
+
requestBuilder := undefined
|
|
209
|
+
|
|
210
|
+
if useBatch(queue_name) {
|
|
211
|
+
requestBuilder = smart.ephemeralBuilder(_RTYPE_COMPUTE_REQUEST_BATCH_V1)
|
|
212
|
+
} else {
|
|
213
|
+
requestBuilder = smart.ephemeralBuilder(_RTYPE_COMPUTE_REQUEST_EXECUTOR_V1)
|
|
214
|
+
}
|
|
191
215
|
|
|
192
|
-
rType := _RTYPE_COMPUTE_REQUEST(executor)
|
|
193
|
-
requestBuilder := smart.ephemeralBuilder(rType)
|
|
194
216
|
requestBuilder.getField(_CR_FIELD_REQUEST).set(quota)
|
|
195
217
|
|
|
218
|
+
requestRef := requestBuilder.lockAndBuild()
|
|
219
|
+
|
|
196
220
|
return ll.toStrict(
|
|
197
|
-
oop.inherit(
|
|
198
|
-
allocation:
|
|
221
|
+
oop.inherit(requestRef, {
|
|
222
|
+
allocation: requestRef.getField(_CR_FIELD_ALLOCATION)
|
|
199
223
|
})
|
|
200
224
|
)
|
|
201
225
|
}
|
|
@@ -205,6 +229,7 @@ computeRequestBuilder := func() {
|
|
|
205
229
|
}
|
|
206
230
|
|
|
207
231
|
export ll.toStrict({
|
|
232
|
+
useBatch: useBatch,
|
|
208
233
|
quotaBuilder: quotaBuilder,
|
|
209
234
|
storageSpaceRequestBuilder: storageSpaceRequestBuilder,
|
|
210
235
|
computeRequestBuilder: computeRequestBuilder
|
|
@@ -15,7 +15,8 @@ slices := import("@platforma-sdk/workflow-tengo:slices")
|
|
|
15
15
|
limits := import("@platforma-sdk/workflow-tengo:exec.limits")
|
|
16
16
|
|
|
17
17
|
_RTYPE_RUN_COMMAND_V1 := { Name: "RunCommand/executor", Version: "1" }
|
|
18
|
-
|
|
18
|
+
_RTYPE_RUN_COMMAND_EXEC_V2 := { Name: "RunCommand/executor", Version: "2" }
|
|
19
|
+
_RTYPE_RUN_COMMAND_BATCH_V2 := { Name: "RunCommand/batch", Version: "2" }
|
|
19
20
|
_RTYPE_RUN_COMMAND_CMD := { Name: "RunCommandCmd", Version: "1" }
|
|
20
21
|
_RTYPE_RUN_COMMAND_ARGS := { Name: "RunCommandArgs", Version: "1" }
|
|
21
22
|
_RTYPE_RUN_COMMAND_OPTIONS := { Name: "run-command/options", Version: "1" }
|
|
@@ -77,16 +78,6 @@ _createExpressionArg := func(argTpl) {
|
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
builder := func(workdir) {
|
|
80
|
-
res := undefined
|
|
81
|
-
|
|
82
|
-
if feats.computeLimits {
|
|
83
|
-
res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V2)
|
|
84
|
-
} else {
|
|
85
|
-
res = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V1)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
res.getField(_FIELD_WORKDIR_IN).set(workdir)
|
|
89
|
-
|
|
90
81
|
self := undefined
|
|
91
82
|
|
|
92
83
|
cmd := undefined
|
|
@@ -105,7 +96,7 @@ builder := func(workdir) {
|
|
|
105
96
|
stderr := "stderr.txt"
|
|
106
97
|
nErrorLines := 200
|
|
107
98
|
|
|
108
|
-
self = ll.toStrict(
|
|
99
|
+
self = ll.toStrict({
|
|
109
100
|
|
|
110
101
|
|
|
111
102
|
|
|
@@ -387,6 +378,25 @@ builder := func(workdir) {
|
|
|
387
378
|
|
|
388
379
|
|
|
389
380
|
build: func() {
|
|
381
|
+
runRes := undefined
|
|
382
|
+
|
|
383
|
+
useBatch := limits.useBatch(queue)
|
|
384
|
+
|
|
385
|
+
if useBatch {
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_BATCH_V2)
|
|
390
|
+
|
|
391
|
+
} else if feats.computeLimits {
|
|
392
|
+
runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_EXEC_V2)
|
|
393
|
+
|
|
394
|
+
} else {
|
|
395
|
+
runRes = smart.ephemeralBuilder(_RTYPE_RUN_COMMAND_V1)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
runRes.getField(_FIELD_WORKDIR_IN).set(workdir)
|
|
399
|
+
|
|
390
400
|
|
|
391
401
|
|
|
392
402
|
ll.assert(!hasPatternArgs || len(substRules) != 0,
|
|
@@ -396,21 +406,23 @@ builder := func(workdir) {
|
|
|
396
406
|
"runcmd.builder().build: expression arguments must be enabled only if platforma backend supports expressions, found: %v",
|
|
397
407
|
hasExpressionArgs)
|
|
398
408
|
|
|
399
|
-
if feats.computeLimits {
|
|
409
|
+
if feats.computeLimits || useBatch {
|
|
400
410
|
if !smart.isReference(_allocation) {
|
|
401
411
|
|
|
402
412
|
|
|
403
|
-
|
|
404
|
-
|
|
413
|
+
_quotaRef := limits.quotaBuilder().queue(queue).build()
|
|
414
|
+
_resReqRef := limits.computeRequestBuilder().queue(queue).quota(_quotaRef).build()
|
|
415
|
+
_allocation = _resReqRef.allocation
|
|
405
416
|
}
|
|
406
|
-
|
|
417
|
+
|
|
418
|
+
runRes.getField(_FIELD_ALLOCATION).set(_allocation)
|
|
407
419
|
}
|
|
408
420
|
|
|
409
421
|
refsRes := smart.createMapResourceWithType(_RTYPE_RUN_COMMAND_REFS, refs)
|
|
410
|
-
|
|
422
|
+
runRes.getField(_FIELD_REFS).set(refsRes)
|
|
411
423
|
|
|
412
424
|
cmdRes := smart.createValueResource(_RTYPE_RUN_COMMAND_CMD, json.encode(cmd))
|
|
413
|
-
|
|
425
|
+
runRes.getField(_FIELD_CMD).set(cmdRes)
|
|
414
426
|
|
|
415
427
|
options := {
|
|
416
428
|
queueName: queue,
|
|
@@ -423,19 +435,19 @@ builder := func(workdir) {
|
|
|
423
435
|
}
|
|
424
436
|
|
|
425
437
|
optionsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_OPTIONS, json.encode(options))
|
|
426
|
-
|
|
438
|
+
runRes.getField(_FIELD_OPTIONS).set(optionsRes)
|
|
427
439
|
|
|
428
440
|
argsRes := smart.createValueResource(_RTYPE_RUN_COMMAND_ARGS, json.encode(args))
|
|
429
|
-
|
|
441
|
+
runRes.getField(_FIELD_ARGS).set(argsRes)
|
|
430
442
|
|
|
431
|
-
res := ll.toStrict(oop.inherit(
|
|
432
|
-
"workdir":
|
|
443
|
+
res := ll.toStrict(oop.inherit(runRes.lockAndBuild(), {
|
|
444
|
+
"workdir": runRes.outputs()[_FIELD_WORKDIR_OUT],
|
|
433
445
|
"options": options
|
|
434
446
|
}))
|
|
435
447
|
|
|
436
448
|
return res
|
|
437
449
|
}
|
|
438
|
-
})
|
|
450
|
+
})
|
|
439
451
|
|
|
440
452
|
return self
|
|
441
453
|
}
|
|
@@ -18,9 +18,25 @@ _isEnabled := func(featName) {
|
|
|
18
18
|
return _checker(featName)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
_executors := undefined
|
|
22
|
+
_hasRunner := func(e_id) {
|
|
23
|
+
if is_undefined(_executors) {
|
|
24
|
+
_executors = {}
|
|
25
|
+
for e in plapi.getExecutors() {
|
|
26
|
+
_executors[e] = true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return !is_undefined(_executors[e_id])
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
export ll.toStrict({
|
|
34
|
+
hasExec: _hasRunner("executor"), // local binary execution is available
|
|
35
|
+
hasBatch: _hasRunner("batch"), // batch system execution is available
|
|
36
|
+
|
|
22
37
|
pureFutureFields: _isEnabled("pureFutureFields") || _api >= 1, // 'pure' future fields magic that provides deduplication
|
|
23
38
|
commandExpressions: _isEnabled("commandExpressions") || _api >= 2, // rich expressions in command arguments ( {{ system.ram_bytes / 20 }} )
|
|
24
39
|
workdirLimits: _isEnabled("workdirLimits") || _api >= 4, // workdir storage controller can limit concurrency
|
|
25
|
-
computeLimits: _isEnabled("computeLimits")
|
|
40
|
+
computeLimits: _isEnabled("computeLimits"), // runner can plan CPU and RAM
|
|
41
|
+
resourceDuplicateEvent: false // template can subscribe to ResourceDuplicate event
|
|
26
42
|
})
|
|
@@ -685,6 +685,22 @@ resource = func(r) {
|
|
|
685
685
|
|
|
686
686
|
|
|
687
687
|
|
|
688
|
+
createDynamicField: func(name) {
|
|
689
|
+
if is_bytes(name) {
|
|
690
|
+
name = string(name)
|
|
691
|
+
}
|
|
692
|
+
ll.assert(is_string(name) && name != "", "field name must be a non-empty string")
|
|
693
|
+
fid := ll.fieldId(self.id, name)
|
|
694
|
+
tx.createField(fid, constants.FTYPE_DYNAMIC)
|
|
695
|
+
return field(fid)
|
|
696
|
+
},
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
688
704
|
|
|
689
705
|
kvGet: func(key) {
|
|
690
706
|
return tx.resourceKeyValueGet(self.id, key)
|
|
@@ -102,7 +102,8 @@ AWAIT_TEMPLATES := {
|
|
|
102
102
|
"PColumnBundleWithPartitions": [
|
|
103
103
|
[{ wildcard: "*" }, { match: "^ref$" }, "ResourceReady"], // optional ref field
|
|
104
104
|
[{ wildcard: "*" }, "spec", "ResourceReady"],
|
|
105
|
-
[{ wildcard: "*" }, "data", "InputsLocked"]
|
|
105
|
+
[{ wildcard: "*" }, "data", "InputsLocked"],
|
|
106
|
+
[{ wildcard: "*" }, "data", { match: "^\\{.*\\}$" }, "InputsLocked"] // for multi-hit BQuery results; key is always ref
|
|
106
107
|
]
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -132,6 +133,9 @@ tpl := func(ops) {
|
|
|
132
133
|
_stateRequests := []
|
|
133
134
|
_delegatedOutputs := []
|
|
134
135
|
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
135
139
|
self = {
|
|
136
140
|
|
|
137
141
|
|
|
@@ -565,7 +569,7 @@ tpl := func(ops) {
|
|
|
565
569
|
if _hasPrepared {
|
|
566
570
|
_stateRequests = append(_stateRequests, {
|
|
567
571
|
path: [tplutil.PREPARE_MAP_FIELD_NAME],
|
|
568
|
-
state: "
|
|
572
|
+
state: "Final"
|
|
569
573
|
})
|
|
570
574
|
}
|
|
571
575
|
|
|
@@ -7,6 +7,9 @@ tx := import("tx")
|
|
|
7
7
|
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
8
8
|
text := import("text")
|
|
9
9
|
regexp := import("@platforma-sdk/workflow-tengo:regexp")
|
|
10
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
11
|
+
maps := import("@platforma-sdk/workflow-tengo:maps")
|
|
12
|
+
feats := import("@platforma-sdk/workflow-tengo:feats")
|
|
10
13
|
|
|
11
14
|
createContext := func() {
|
|
12
15
|
return {
|
|
@@ -15,6 +18,9 @@ createContext := func() {
|
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
|
|
22
|
+
RTYPE_AWAITER := { Name: "awaiter", Version: "1" }
|
|
23
|
+
|
|
18
24
|
awaitOneState := func(field, path, state) {
|
|
19
25
|
if field.isErrorSet() && !field.isValueSet() {
|
|
20
26
|
|
|
@@ -51,9 +57,6 @@ awaitOneState := func(field, path, state) {
|
|
|
51
57
|
subscriptions: [{
|
|
52
58
|
resource: resource.id,
|
|
53
59
|
event: "FieldCreated"
|
|
54
|
-
},{
|
|
55
|
-
resource: resource.id,
|
|
56
|
-
event: "InputsLocked"
|
|
57
60
|
}]
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -93,19 +96,19 @@ awaitOneState := func(field, path, state) {
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
subscriptions := []
|
|
96
|
-
|
|
99
|
+
maps.forEach(resource.inputs(), func(nextFieldName, nextField) {
|
|
97
100
|
if !matcher(nextFieldName) {
|
|
98
|
-
|
|
101
|
+
return
|
|
99
102
|
}
|
|
100
103
|
res := awaitOneState(nextField, rPath, state)
|
|
101
104
|
if res.type == "Ready" {
|
|
102
|
-
|
|
105
|
+
return
|
|
103
106
|
} else if res.type == "Subscribe" {
|
|
104
107
|
subscriptions = append(subscriptions, res.subscriptions...)
|
|
105
108
|
} else {
|
|
106
109
|
ll.panic("Unknown result type: %v", res.type)
|
|
107
110
|
}
|
|
108
|
-
}
|
|
111
|
+
})
|
|
109
112
|
if len(subscriptions) == 0 {
|
|
110
113
|
return { type: "Ready" }
|
|
111
114
|
} else {
|
|
@@ -157,7 +160,7 @@ awaitOneState := func(field, path, state) {
|
|
|
157
160
|
}]
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
|
-
} else if state == "ResourceReady" {
|
|
163
|
+
} else if state == "ResourceReady" || state == "Final" {
|
|
161
164
|
|
|
162
165
|
|
|
163
166
|
if resource.isFinal() {
|
|
@@ -167,7 +170,7 @@ awaitOneState := func(field, path, state) {
|
|
|
167
170
|
type: "Subscribe",
|
|
168
171
|
subscriptions: [{
|
|
169
172
|
resource: resource.id,
|
|
170
|
-
event: "
|
|
173
|
+
event: "Final" // will also effectively subscribe to ResourceDuplicate and ResourceError
|
|
171
174
|
}]
|
|
172
175
|
}
|
|
173
176
|
}
|
|
@@ -178,19 +181,21 @@ awaitOneState := func(field, path, state) {
|
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
|
|
184
|
+
_renderer := smart.resource(ll.getCurrentTemplateRenderer())
|
|
185
|
+
|
|
181
186
|
|
|
182
187
|
await := func(rootField, ctxStorageResource, requests) {
|
|
183
188
|
pssKey := "await:" + string(rootField.id.ResourceID) + ":" + rootField.id.Name
|
|
184
189
|
prevSubscriptions := ctxStorageResource.kvGetAsJson(pssKey)
|
|
185
190
|
|
|
186
|
-
|
|
191
|
+
lastSubscriptionKey := undefined
|
|
192
|
+
if !is_undefined(prevSubscriptions) {
|
|
193
|
+
if is_undefined(prevSubscriptions.lastSubscriptionKey) {
|
|
187
194
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
prevSubscriptions = {}
|
|
195
|
+
ll.panic("Unexpected state, desired state was reached in the previous call")
|
|
196
|
+
} else {
|
|
197
|
+
lastSubscriptionKey = prevSubscriptions.lastSubscriptionKey
|
|
198
|
+
}
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
subscriptions := {}
|
|
@@ -222,34 +227,88 @@ await := func(rootField, ctxStorageResource, requests) {
|
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
|
|
225
|
-
for key, sub in subscriptions {
|
|
226
|
-
if !is_undefined(prevSubscriptions[key]) {
|
|
227
230
|
|
|
228
|
-
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
firstSubscriptionKey := undefined
|
|
238
|
+
firstSubscription := undefined
|
|
239
|
+
lastSubscriptionKeyPresent := false
|
|
240
|
+
maps.forEach(subscriptions, func(key, sub) {
|
|
241
|
+
if is_undefined(firstSubscriptionKey) {
|
|
242
|
+
firstSubscriptionKey = key
|
|
243
|
+
firstSubscription = sub
|
|
229
244
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
245
|
+
if !is_undefined(lastSubscriptionKey) && lastSubscriptionKey == key {
|
|
246
|
+
lastSubscriptionKeyPresent = true
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
if lastSubscriptionKeyPresent {
|
|
253
|
+
return false
|
|
233
254
|
}
|
|
234
255
|
|
|
235
256
|
|
|
236
|
-
|
|
237
|
-
if !is_undefined(subscriptions[key]) {
|
|
257
|
+
if !is_undefined(firstSubscriptionKey) {
|
|
238
258
|
|
|
239
|
-
|
|
259
|
+
fullEventId := pssKey + ":" + firstSubscriptionKey
|
|
260
|
+
targetResource := firstSubscription.resource
|
|
261
|
+
targetEvents := [firstSubscription.event]
|
|
262
|
+
|
|
263
|
+
if firstSubscription.event == "Final" { // special case
|
|
264
|
+
if !feats.resourceDuplicateEvent {
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
target := smart.resource(targetResource)
|
|
272
|
+
isEphemeral := target.info().Features.ephemeral
|
|
273
|
+
if !isEphemeral { // doing proxy-object workaround for pure resources only
|
|
274
|
+
awaiterBuilder := smart.ephemeralBuilder(RTYPE_AWAITER)
|
|
275
|
+
awaiterBuilder.createInputField("target").set(target)
|
|
276
|
+
awaiter := awaiterBuilder.lockAndBuild()
|
|
277
|
+
_renderer.createDynamicField("tmp:" + fullEventId).set(awaiter)
|
|
278
|
+
targetResource = awaiter.info().ID // converting to global resource id; this is a hack to overcome the fact that tx.subscribeTo can't work with local ids
|
|
279
|
+
}
|
|
280
|
+
targetEvents = ["ResourceReady", "ResourceError"]
|
|
281
|
+
|
|
282
|
+
} else {
|
|
283
|
+
targetEvents = ["ResourceReady", "ResourceError", "ResourceDuplicate"]
|
|
284
|
+
}
|
|
285
|
+
} else if firstSubscription.event == "FieldCreated" { // another special case
|
|
286
|
+
|
|
287
|
+
targetEvents = ["FieldCreated", "InputsLocked"]
|
|
240
288
|
}
|
|
241
|
-
|
|
289
|
+
|
|
290
|
+
e := {}
|
|
291
|
+
for event in targetEvents {
|
|
292
|
+
e[event] = true
|
|
293
|
+
}
|
|
294
|
+
tx.subscribeTo(targetResource, fullEventId, e)
|
|
295
|
+
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
if !is_undefined(lastSubscriptionKey) {
|
|
300
|
+
tx.unsubscribeFrom(pssKey + ":" + lastSubscriptionKey)
|
|
242
301
|
}
|
|
243
302
|
|
|
244
303
|
|
|
245
304
|
newPrevSubscriptions := {}
|
|
246
|
-
|
|
247
|
-
newPrevSubscriptions
|
|
305
|
+
if !is_undefined(firstSubscriptionKey) {
|
|
306
|
+
newPrevSubscriptions.lastSubscriptionKey = firstSubscriptionKey
|
|
248
307
|
}
|
|
249
308
|
|
|
250
309
|
ctxStorageResource.kvSetJson(pssKey, newPrevSubscriptions)
|
|
251
310
|
|
|
252
|
-
return
|
|
311
|
+
return is_undefined(firstSubscriptionKey)
|
|
253
312
|
}
|
|
254
313
|
|
|
255
314
|
export ll.toStrict({
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|