@platforma-sdk/workflow-tengo 4.7.0 → 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/feats.lib.tengo +2 -1
- package/dist/tengo/lib/smart.lib.tengo +16 -0
- package/dist/tengo/lib/tpl.base.lib.tengo +3 -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
|
@@ -37,5 +37,6 @@ export ll.toStrict({
|
|
|
37
37
|
pureFutureFields: _isEnabled("pureFutureFields") || _api >= 1, // 'pure' future fields magic that provides deduplication
|
|
38
38
|
commandExpressions: _isEnabled("commandExpressions") || _api >= 2, // rich expressions in command arguments ( {{ system.ram_bytes / 20 }} )
|
|
39
39
|
workdirLimits: _isEnabled("workdirLimits") || _api >= 4, // workdir storage controller can limit concurrency
|
|
40
|
-
computeLimits: _isEnabled("computeLimits")
|
|
40
|
+
computeLimits: _isEnabled("computeLimits"), // runner can plan CPU and RAM
|
|
41
|
+
resourceDuplicateEvent: false // template can subscribe to ResourceDuplicate event
|
|
41
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
|
|
|
@@ -568,7 +569,7 @@ tpl := func(ops) {
|
|
|
568
569
|
if _hasPrepared {
|
|
569
570
|
_stateRequests = append(_stateRequests, {
|
|
570
571
|
path: [tplutil.PREPARE_MAP_FIELD_NAME],
|
|
571
|
-
state: "
|
|
572
|
+
state: "Final"
|
|
572
573
|
})
|
|
573
574
|
}
|
|
574
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
|