@platforma-sdk/workflow-tengo 2.9.6 → 2.9.7

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 (64) hide show
  1. package/dist/tengo/lib/assets.lib.tengo +52 -52
  2. package/dist/tengo/lib/assets.swinfo.lib.tengo +45 -45
  3. package/dist/tengo/lib/constants.lib.tengo +32 -32
  4. package/dist/tengo/lib/exec.R.internal.lib.tengo +29 -29
  5. package/dist/tengo/lib/exec.R.lib.tengo +166 -166
  6. package/dist/tengo/lib/exec.constants.lib.tengo +4 -4
  7. package/dist/tengo/lib/exec.descriptor.lib.tengo +31 -31
  8. package/dist/tengo/lib/exec.internal.lib.tengo +297 -297
  9. package/dist/tengo/lib/exec.lib.tengo +815 -815
  10. package/dist/tengo/lib/exec.monetization.lib.tengo +130 -130
  11. package/dist/tengo/lib/exec.package.lib.tengo +212 -212
  12. package/dist/tengo/lib/exec.python.internal.lib.tengo +6 -6
  13. package/dist/tengo/lib/exec.python.lib.tengo +146 -146
  14. package/dist/tengo/lib/exec.runcmd.lib.tengo +371 -371
  15. package/dist/tengo/lib/exec.runenv.lib.tengo +92 -92
  16. package/dist/tengo/lib/exec.stream.lib.tengo +40 -40
  17. package/dist/tengo/lib/file.internal.lib.tengo +77 -77
  18. package/dist/tengo/lib/file.lib.tengo +57 -57
  19. package/dist/tengo/lib/ll.lib.tengo +244 -244
  20. package/dist/tengo/lib/maps.lib.tengo +152 -152
  21. package/dist/tengo/lib/objects.lib.tengo +13 -13
  22. package/dist/tengo/lib/oop.lib.tengo +19 -19
  23. package/dist/tengo/lib/path.lib.tengo +125 -125
  24. package/dist/tengo/lib/pframes.constants.lib.tengo +28 -28
  25. package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +33 -33
  26. package/dist/tengo/lib/pframes.lib.tengo +43 -43
  27. package/dist/tengo/lib/pframes.ll.lib.tengo +72 -72
  28. package/dist/tengo/lib/pframes.pcolumn.lib.tengo +49 -49
  29. package/dist/tengo/lib/pframes.spec.lib.tengo +96 -96
  30. package/dist/tengo/lib/pframes.util.lib.tengo +288 -288
  31. package/dist/tengo/lib/pframes.xsv.lib.tengo +272 -272
  32. package/dist/tengo/lib/render.lib.tengo +75 -75
  33. package/dist/tengo/lib/sets.lib.tengo +68 -68
  34. package/dist/tengo/lib/slices.lib.tengo +258 -155
  35. package/dist/tengo/lib/smart.lib.tengo +1069 -1069
  36. package/dist/tengo/lib/test.lib.tengo +161 -161
  37. package/dist/tengo/lib/tpl.lib.tengo +515 -515
  38. package/dist/tengo/lib/tpl.state-await.lib.tengo +240 -240
  39. package/dist/tengo/lib/tpl.util.lib.tengo +9 -9
  40. package/dist/tengo/lib/validation.lib.tengo +378 -378
  41. package/dist/tengo/lib/workdir.lib.tengo +331 -331
  42. package/dist/tengo/lib/workdir.proc.lib.tengo +144 -144
  43. package/dist/tengo/lib/workdir.save.lib.tengo +130 -130
  44. package/dist/tengo/lib/workflow.bobject.lib.tengo +26 -26
  45. package/dist/tengo/lib/workflow.bquery.lib.tengo +165 -165
  46. package/dist/tengo/lib/workflow.constants.lib.tengo +2 -2
  47. package/dist/tengo/lib/workflow.context.lib.tengo +32 -32
  48. package/dist/tengo/lib/workflow.lib.tengo +206 -206
  49. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  50. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  51. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  52. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  53. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  54. package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
  55. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  56. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  57. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  58. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  59. package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
  60. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
  61. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  62. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  63. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  64. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
-
2
-
3
-
1
+ /*
2
+ * Library provides smart.Resource and smart.Field primitives.
3
+ */
4
4
 
5
5
  tx := import("tx")
6
6
  ll := import("@platforma-sdk/workflow-tengo:ll")
@@ -10,1173 +10,1173 @@ json := import("json")
10
10
  times := import("times")
11
11
  constants := import("@platforma-sdk/workflow-tengo:constants")
12
12
 
13
+ //////////////// definitions ////////////////
13
14
 
14
-
15
-
16
-
17
-
15
+ /**
16
+ * A read-only wrapper object around raw platforma resource
17
+ */
18
18
  resource := undefined
19
19
 
20
-
21
-
22
-
20
+ /**
21
+ * A wrapper object around raw platforma resource field
22
+ */
23
23
  field := undefined
24
24
 
25
-
26
-
27
-
25
+ /**
26
+ * A convenient builder for resource objects
27
+ */
28
28
  resourceBuilder := undefined
29
29
 
30
-
31
-
32
-
33
-
34
-
30
+ /**
31
+ * Builder for structural resource
32
+ *
33
+ * @param resourceType { Name: "", Version: "" }
34
+ */
35
35
  structBuilder := undefined
36
36
 
37
-
38
-
39
-
37
+ /**
38
+ * Builder for map resource
39
+ */
40
40
  mapBuilder := undefined
41
41
 
42
-
43
-
44
-
42
+ /**
43
+ * Builder for ephemeral map resource
44
+ */
45
45
  ephemeralMapBuilder := undefined
46
46
 
47
-
48
-
49
-
50
-
51
-
47
+ /**
48
+ * Builder for emphemeral resources
49
+ *
50
+ * @param resourceType { Name: "", Version: "" }
51
+ */
52
52
  ephemeralBuilder := undefined
53
53
 
54
-
55
-
56
-
57
-
58
-
59
-
54
+ /**
55
+ * Create value resource
56
+ *
57
+ * @param resourceType { Name: "", Version: "" }
58
+ * @param data optional data
59
+ */
60
60
  createValueResource := undefined
61
61
 
62
-
63
-
64
-
65
-
66
-
62
+ /**
63
+ * Create Json resource
64
+ *
65
+ * @param value value to save in the resource
66
+ */
67
67
  createJsonResource := undefined
68
68
 
69
-
70
-
71
-
72
-
73
-
74
-
69
+ /**
70
+ * Create map resource
71
+ *
72
+ * @param keyToValue: map[string]anyResource
73
+ * @return resource
74
+ */
75
75
  createMapResource := undefined
76
76
 
77
-
78
-
79
-
80
-
81
-
82
-
77
+ /**
78
+ * Create binary map resource
79
+ *
80
+ * @param keyToValue: map[string]valueResource
81
+ * @return resource
82
+ */
83
83
  createBinaryMapResource := undefined
84
84
 
85
-
86
-
87
-
88
-
89
-
90
-
85
+ /**
86
+ * Create blob map resource
87
+ *
88
+ * @param keyToValue: map[string]blobResource
89
+ * @return resource
90
+ */
91
91
  createBlobMapResource := undefined
92
92
 
93
+ ////////////////// helpers ///////////////////////////
93
94
 
94
-
95
-
96
-
97
-
95
+ /**
96
+ * Returns true if given object is a smart.Resource
97
+ */
98
98
  isResource := func(f) {
99
- return ll.isStrict(f) && maps.containsKey(f, "_type") && f["_type"] == "resource"
99
+ return ll.isStrict(f) && maps.containsKey(f, "_type") && f["_type"] == "resource"
100
100
  }
101
101
 
102
-
103
-
104
-
102
+ /**
103
+ * Returns true if given object is a smart.Field
104
+ */
105
105
  isField := func(f) {
106
- return ll.isStrict(f) && maps.containsKey(f, "_type") && f["_type"] == "field"
106
+ return ll.isStrict(f) && maps.containsKey(f, "_type") && f["_type"] == "field"
107
107
  }
108
108
 
109
-
110
-
111
-
109
+ /**
110
+ * Returns true if given object is a smart.Field or smart.Resource
111
+ */
112
112
  isReference := func(f) {
113
- return isField(f) || isResource(f)
114
- }
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
113
+ return isField(f) || isResource(f)
114
+ }
115
+
116
+ /**
117
+ * Gets future field by given field name and field type.
118
+ *
119
+ * @param from: reference - whether a resource or a field we get a field from.
120
+ * @param name: string - field name
121
+ * @param fieldType: field type: input, output
122
+ * @param isEph: bool - resolve in ephemeral or pure mode
123
+ *
124
+ * @return field: smart field
125
+ */
126
126
  _getFutureField := func(from, name, fieldType, isEph) {
127
- ll.assert(isReference(from), "'from' must be either a resource or a field, got: %#v", from)
127
+ ll.assert(isReference(from), "'from' must be either a resource or a field, got: %#v", from)
128
128
 
129
- futureField := tx.createFutureFieldID(fieldType, name, !isEph)
130
- f := field(futureField.ResourceFID)
131
- f.set(from)
129
+ futureField := tx.createFutureFieldID(fieldType, name, !isEph)
130
+ f := field(futureField.ResourceFID)
131
+ f.set(from)
132
132
 
133
- return field(futureField.ResultFID)
133
+ return field(futureField.ResultFID)
134
134
  }
135
-
136
-
137
-
135
+ /**
136
+ * Returns true if given object is a StdMap resource
137
+ */
138
138
  isMapResource := func(r) {
139
- return isResource(r) && (
140
- r.info().Type.Name == constants.RTYPE_MAP.Name ||
141
- r.info().Type.Name == constants.RTYPE_MAP_ALT.Name) // @TODO get rid of alt name
139
+ return isResource(r) && (
140
+ r.info().Type.Name == constants.RTYPE_MAP.Name ||
141
+ r.info().Type.Name == constants.RTYPE_MAP_ALT.Name) // @TODO get rid of alt name
142
142
 
143
143
  }
144
144
 
145
-
146
-
147
-
145
+ /**
146
+ * Returns true if given object is an ephemeral std map resource
147
+ */
148
148
  isEphMapResource := func(r) {
149
- return isResource(r) && r.info().Type.Name == constants.RTYPE_MAP_EPH.Name
149
+ return isResource(r) && r.info().Type.Name == constants.RTYPE_MAP_EPH.Name
150
150
  }
151
151
 
152
-
153
-
154
-
152
+ /**
153
+ * Returns true if given object is an ephemeral std map resource with locked inputs (so the list of inputs is final)
154
+ */
155
155
  isLockedEphMapResource := func(r) {
156
- return isEphMapResource(r) && r.info().InputsLocked
156
+ return isEphMapResource(r) && r.info().InputsLocked
157
157
  }
158
158
 
159
-
160
-
161
-
159
+ /**
160
+ * Returns true if given object is a map or ephemeral map resource
161
+ */
162
162
  isAnyMapResource := func(r) {
163
- return isMapResource(r) || isEphMapResource(r)
163
+ return isMapResource(r) || isEphMapResource(r)
164
164
  }
165
165
 
166
-
167
-
168
-
166
+ /**
167
+ * Returns true if given object is a json resource
168
+ */
169
169
  isJsonResource := func(r) {
170
- return isResource(r) && r.info().Type.Name == constants.RTYPE_JSON.Name
170
+ return isResource(r) && r.info().Type.Name == constants.RTYPE_JSON.Name
171
171
  }
172
172
 
173
-
173
+ // deep check whether map has resources somewhere inside
174
174
  _hasReferenceValues := func(object) {
175
- if isReference(object) {
176
-
177
- return true
178
- } else if ll.isMap(object) {
179
- for _, v in object {
180
- if _hasReferenceValues(v) {
181
- return true
182
- }
183
- }
184
- } else if is_array(object) {
185
- for v in object {
186
- if _hasReferenceValues(v) {
187
- return true
188
- }
189
- }
190
- }
191
- return false
192
- }
193
-
194
-
195
-
196
-
197
-
175
+ if isReference(object) {
176
+ // first check is reference since resource/field will return true on ll.isMap
177
+ return true
178
+ } else if ll.isMap(object) {
179
+ for _, v in object {
180
+ if _hasReferenceValues(v) {
181
+ return true
182
+ }
183
+ }
184
+ } else if is_array(object) {
185
+ for v in object {
186
+ if _hasReferenceValues(v) {
187
+ return true
188
+ }
189
+ }
190
+ }
191
+ return false
192
+ }
193
+
194
+ /**
195
+ * Convert given object to resource. If object is a true json, then
196
+ * a simple Json resource will be created, otherwise a resource map.
197
+ */
198
198
  _toRefOrJson := func(object) {
199
- if isReference(object) {
200
-
201
- return object
202
- } else if ll.isMap(object) {
203
-
204
-
205
- if !_hasReferenceValues(object) {
206
- return createJsonResource(object)
207
- }
199
+ if isReference(object) {
200
+ // first check is reference since resource/field will return true on ll.isMap
201
+ return object
202
+ } else if ll.isMap(object) {
208
203
 
209
- mb := mapBuilder()
210
- for k, v in object {
211
- mb.add(k, _toRefOrJson(v))
212
- }
204
+ // just json if there are no nested resources
205
+ if !_hasReferenceValues(object) {
206
+ return createJsonResource(object)
207
+ }
213
208
 
214
- return mb.build()
215
- } else if is_array(object) {
209
+ mb := mapBuilder()
210
+ for k, v in object {
211
+ mb.add(k, _toRefOrJson(v))
212
+ }
216
213
 
214
+ return mb.build()
215
+ } else if is_array(object) {
217
216
 
218
- ll.assert(!_hasReferenceValues(object), "array can't contain references")
217
+ // assert we are not having refs inside
218
+ ll.assert(!_hasReferenceValues(object), "array can't contain references")
219
219
 
220
- return createJsonResource(object)
221
- } else {
222
- return createJsonResource(object)
223
- }
220
+ return createJsonResource(object)
221
+ } else {
222
+ return createJsonResource(object)
223
+ }
224
224
  }
225
225
 
226
226
  resourceTypesEqual := func(rType1, rType2) {
227
- return rType1.Name == rType2.Name && rType1.Version == rType2.Version
227
+ return rType1.Name == rType2.Name && rType1.Version == rType2.Version
228
228
  }
229
229
 
230
+ ////////////////// implementation ///////////////////////////
230
231
 
231
-
232
-
233
-
234
-
232
+ /**
233
+ * @param r either resource id or resource info
234
+ */
235
235
  resource = func(r) {
236
- self := undefined
237
- strictSelf := undefined
238
-
239
- if isResource(r) {
240
- return r
241
- }
242
-
243
- id := ll.isStrict(r) ? r.ID : r
244
-
245
- if !is_int(id) {
246
- ll.panic("resource id should be int for ", r)
247
- }
248
-
249
- if id == 0 {
250
- ll.panic("null resource id for ", r)
251
- }
252
-
253
- self = {
254
- _type: "resource",
255
-
256
-
257
-
258
-
259
- id: id,
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
- info: func() {
281
- return tx.getResource(self.id)
282
- },
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
- checkResourceType: func(expectedType) {
291
- return resourceTypesEqual(self.info().Type, expectedType)
292
- },
293
-
294
-
295
-
296
-
297
- isReady: func() {
298
- return self.info().ResourceReady
299
- },
300
-
301
- isEphemeral: func() {
302
- resourceInfo := self.info()
303
-
304
- if !maps.containsKey(resourceInfo, "Features") {
305
-
306
-
307
-
308
- return true
236
+ self := undefined
237
+ strictSelf := undefined
238
+
239
+ if isResource(r) {
240
+ return r
241
+ }
242
+
243
+ id := ll.isStrict(r) ? r.ID : r
244
+
245
+ if !is_int(id) {
246
+ ll.panic("resource id should be int for ", r)
247
+ }
248
+
249
+ if id == 0 {
250
+ ll.panic("null resource id for ", r)
251
+ }
252
+
253
+ self = {
254
+ _type: "resource",
255
+
256
+ /**
257
+ * Resource id (int64)
258
+ */
259
+ id: id,
260
+
261
+ /**
262
+ * Resource info:
263
+ *
264
+ * ID: int64
265
+ * Data: byte[],
266
+ * Type: { Name: string, Version: string }
267
+ * Features: { ephemeral: bool }
268
+ * Inputs: strict map
269
+ * Outputs: strict map
270
+ * Dynamic: strict map
271
+ * OTW: strict map
272
+ * ResourceReady: bool
273
+ * AllInputsSet: bool
274
+ * AllOutputsSet: bool
275
+ * IsDuplicate: bool
276
+ * InputsLocked: bool
277
+ * OutputsLocked: bool
278
+ * HasErrors: bool
279
+ */
280
+ info: func() {
281
+ return tx.getResource(self.id)
282
+ },
283
+
284
+ /**
285
+ * Returns true if resource has type equal to the one provided as an agrument.
286
+ *
287
+ * @param expectedType type to compare resource type with
288
+ * @return true if type matches, false otherwise
289
+ */
290
+ checkResourceType: func(expectedType) {
291
+ return resourceTypesEqual(self.info().Type, expectedType)
292
+ },
293
+
294
+ /**
295
+ * Resource is ready when all inputs are set to the terminal values.
296
+ */
297
+ isReady: func() {
298
+ return self.info().ResourceReady
299
+ },
300
+
301
+ isEphemeral: func() {
302
+ resourceInfo := self.info()
303
+
304
+ if !maps.containsKey(resourceInfo, "Features") {
305
+ // by default we consider all resources to be ephemeral for backward compatiblity to keep stable logic
306
+ // in platforma backend before v1.16.0.
307
+ // TODO: once we consider this version old enough, we can panic here instead of 'return true'
308
+ return true
309
+ }
310
+
311
+ return resourceInfo.Features.ephemeral
312
+ },
313
+
314
+ /**
315
+ * Get raw resource data
316
+ *
317
+ * @return data: []byte - raw resource data
318
+ */
319
+ getData: func() {
320
+ if self.hasError() {
321
+ ll.panic("resource has errors: ", self.info())
322
+ }
323
+ return self.info().Data;
324
+ },
325
+
326
+ /**
327
+ * Get resource data as Json
328
+ */
329
+ getDataAsJson: func() {
330
+ return json.decode(self.getData());
331
+ },
332
+
333
+ /**
334
+ * Whether this resource has error
335
+ */
336
+ hasError: func() {
337
+ return self._hasField("OneTimeWritables", "resourceError")
338
+ },
339
+
340
+ /**
341
+ * Whether any of the input fields have errors. The resource itself may still not have
342
+ * error status because error propagation was not yet happened.
343
+ */
344
+ hasInputErrors: func() {
345
+ return self.info().HasErrors
346
+ },
347
+
348
+ /**
349
+ * Return error set in the resource
350
+ */
351
+ getError: func() {
352
+ if !self.info().HasErrors {
353
+ ll.panic("no error set in resource")
354
+ }
355
+ return self.getField("resourceError").getError()
356
+ },
357
+
358
+ _hasField: func(fieldMapName, fieldName) {
359
+ return ll.fromStrict(self.info()[fieldMapName])[fieldName] != undefined
360
+ },
361
+
362
+ /**
363
+ * Whether resource has input field with specified name
364
+ *
365
+ * @param name field name
366
+ */
367
+ hasInputField: func(name) {
368
+ return self._hasField("Inputs", name)
369
+ },
370
+
371
+ /**
372
+ * Whether resource has output field with specified name
373
+ *
374
+ * @param name field name
375
+ */
376
+ hasOutputField: func(name) {
377
+ return self._hasField("Outputs", name)
378
+ },
379
+
380
+ /**
381
+ * Whether resource has any field with specified name
382
+ *
383
+ * @param name field name
384
+ */
385
+ hasField: func(name) {
386
+ return self._hasField("Inputs", name) ||
387
+ self._hasField("Outputs", name) ||
388
+ self._hasField("DynamicFields", name) ||
389
+ self._hasField("OneTimeWritables", name)
390
+ },
391
+
392
+ // cached _inputs
393
+ _inputs: undefined,
394
+ /**
395
+ * Returns resource inputs map name -> smart.Field
396
+ */
397
+ inputs: func() {
398
+ if self._inputs == undefined {
399
+ info := self.info()
400
+ inputs := maps.mapValues(info.Inputs, field)
401
+ if !info.InputsLocked {
402
+ return inputs
403
+ }
404
+ self._inputs = inputs
405
+ }
406
+ return self._inputs
407
+ },
408
+
409
+ // cached _outputs
410
+ _outputs: undefined,
411
+ /**
412
+ * Returns resource outputs map name -> smart.Field
413
+ */
414
+ outputs: func() {
415
+ if(self._outputs == undefined) {
416
+ info := self.info()
417
+ outputs := ll.toStrict(maps.mapValues(info.Outputs, field))
418
+ if !info.OutputsLocked {
419
+ return outputs
420
+ }
421
+ self._outputs = outputs
422
+ }
423
+ return self._outputs
424
+ },
425
+
426
+ /**
427
+ * Get smart.Field by name
428
+ *
429
+ * @param name field name
430
+ */
431
+ getField: func(name) {
432
+ if(!self.hasField(name)) {
433
+ ll.panic("field %v is not found", name)
434
+ }
435
+
436
+ return field(ll.fieldId(self.id, name))
437
+ },
438
+
439
+ /**
440
+ * Get future field given field name and field type.
441
+ *
442
+ * @param name: string - field name
443
+ * @param fieldType: field type - input, output
444
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
445
+ * to pass deduplication before resolution.
446
+ *
447
+ * @return field: smart.field
448
+ */
449
+ getFutureField: func(name, fieldType, ...eph) {
450
+ isEph := strictSelf.isEphemeral()
451
+ if len(eph) > 0 {
452
+ isEph = eph[0]
453
+ }
454
+ return _getFutureField(strictSelf, name, fieldType, isEph)
455
+ },
456
+
457
+ /**
458
+ * Get future input field given field name or a path
459
+ *
460
+ * @param nameOrPath: string|string[] - field name or an array representing a path to be traversed
461
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
462
+ * to pass deduplication before resolution.
463
+ *
464
+ * @return field: smart.field
465
+ */
466
+ getFutureInputField: func(nameOrPath, ...eph) {
467
+ isEph := strictSelf.isEphemeral()
468
+ if len(eph) > 0 {
469
+ isEph = eph[0]
470
+ }
471
+
472
+ if is_array(nameOrPath) {
473
+ current := strictSelf
474
+ for name in nameOrPath {
475
+ current = current.getFutureField(name, "input", isEph)
476
+ }
477
+ return current
478
+ } else {
479
+ return self.getFutureField(nameOrPath, "input", isEph)
480
+ }
481
+ },
482
+
483
+ /**
484
+ * Get future output field given field name
485
+ *
486
+ * @param name: string - field name
487
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
488
+ * to pass deduplication before resolution.
489
+ *
490
+ * @return field: smart.field
491
+ */
492
+ getFutureOutputField: func(name, ...eph) {
493
+ isEph := strictSelf.isEphemeral()
494
+ if len(eph) > 0 {
495
+ isEph = eph[0]
496
+ }
497
+
498
+ if ll.apiVersion() < 1 && !isEph {
499
+ // Dirty hack that saves from issues with buggy pure getField before v1.16.1
500
+ // We can safely drop it when we consider v1.16.0 old enough.
501
+ return self.getFutureField(name, "output", true).expectNonEphemeral()
502
+ }
503
+
504
+ return self.getFutureField(name, "output", isEph)
505
+ },
506
+
507
+ /**
508
+ * Get a resource set in field of a given field name
509
+ *
510
+ * @param name field name
511
+ */
512
+ get: func(name) {
513
+ return self.getField(name).getValue()
514
+ },
515
+
516
+ /**
517
+ * Gets value from key value storage associated with resource if corresponding
518
+ * record exists.
519
+ *
520
+ * @param key string key
521
+ * @return bytes or undefined
522
+ */
523
+ kvGet: func(key) {
524
+ return tx.resourceKeyValueGet(self.id, key)
525
+ },
526
+
527
+ /**
528
+ * Sets bytes or string value to a key value storage associated
529
+ * with resource.
530
+ *
531
+ * @param key string key
532
+ * @param value to be set
533
+ */
534
+ kvSet: func(key, value) {
535
+ tx.resourceKeyValueSet(self.id, key, value)
536
+ },
537
+
538
+ /**
539
+ * Gets value as string from key value storage associated with resource if
540
+ * corresponding record exists.
541
+ *
542
+ * @param key string key
543
+ * @return string or undefined
544
+ */
545
+ kvGetAsString: func(key) {
546
+ value := tx.resourceKeyValueGet(self.id, key)
547
+ if is_undefined(value) {
548
+ return undefined
549
+ } else {
550
+ return string(value)
551
+ }
552
+ },
553
+
554
+ /**
555
+ * Gets value from key value storage associated with resource and
556
+ * decodes as json if corresponding record exists.
557
+ *
558
+ * @param key string key
559
+ * @return value or undefined
560
+ */
561
+ kvGetAsJson: func(key) {
562
+ bytes := self.kvGet(key)
563
+ if is_undefined(bytes){
564
+ return undefined
565
+ } else {
566
+ return json.decode(bytes)
567
+ }
568
+ },
569
+
570
+ /**
571
+ * Sets json value to a key value storage associated with resource.
572
+ *
573
+ * @param key string key
574
+ * @param value to be set
575
+ */
576
+ kvSetJson: func(key, value) {
577
+ self.kvSet(key, json.encode(value))
578
+ }
579
+ }
580
+
581
+ strictSelf = ll.toStrict(self)
582
+
583
+ return strictSelf
309
584
  }
310
585
 
311
- return resourceInfo.Features.ephemeral
312
- },
313
-
314
-
315
-
316
-
317
-
586
+ field = func(f) {
587
+ self := undefined
588
+ strictSelf := undefined
589
+
590
+ id := ll.isFieldId(f) ? f : f.ID
591
+
592
+ if (is_undefined(id)) {
593
+ ll.panic("undefined id in smart.field. <f>="+f)
594
+ }
595
+
596
+ self = {
597
+ _type: "field",
598
+
599
+ /**
600
+ * Field id struct:
601
+ *
602
+ * ResourceID: int64
603
+ * Name: string
604
+ *
605
+ */
606
+ id: id,
607
+
608
+ /**
609
+ * Field name
610
+ */
611
+ name: func() {
612
+ return self.id.Name
613
+ },
614
+
615
+ /**
616
+ * Associated smart.Resource
617
+ */
618
+ resource: func() {
619
+ return resource(self.id.ResourceID)
620
+ },
621
+
622
+ /**
623
+ * Field info:
624
+ *
625
+ * ID: { ResourceId: int64, Name: string }
626
+ * Type: string - see constants.FTYPE_* for possible values
627
+ * Features: { ephemeral: bool }
628
+ * Value: int64 - value resource id
629
+ * Error: int64 - error resource id
630
+ * IsSet: bool - whether field is set
631
+ */
632
+ info: func() {
633
+ return tx.getField(self.id)
634
+ },
635
+
636
+ /**
637
+ * Whether the field points to resource or error
638
+ */
639
+ isSet: func() {
640
+ return self.info().IsSet
641
+ },
642
+
643
+ /**
644
+ * Whether field is resolved to a value
645
+ */
646
+ isValueSet: func() {
647
+ return self.info().Value != 0
648
+ },
649
+
650
+ /**
651
+ * Whether field is resolved to a error
652
+ */
653
+ isErrorSet: func() {
654
+ return self.info().Error != 0
655
+ },
656
+
657
+ /**
658
+ * Some fields cannot point to ephemeral resources.
659
+ * This method checks, if current field can.
660
+ *
661
+ * @return bool - "true' when field can point to ephemeral resource, 'false' when not
662
+ */
663
+ isEphemeral: func() {
664
+ fieldInfo := self.info()
665
+
666
+ if !maps.containsKey(fieldInfo, "Features") {
667
+ // by default we consider all fields can point to ephemeral resources for backward compatiblity to keep stable logic
668
+ // in platforma backend before v1.16.0.
669
+ // TODO: once we consider this version old enough, we can panic here instead of 'return true'
670
+ return true
671
+ }
672
+
673
+ isParentResourceEphemeral := fieldInfo.Features.ephemeral
674
+ if isParentResourceEphemeral {
675
+ return true
676
+ }
677
+
678
+ return fieldInfo.Type != constants.FTYPE_INPUT &&
679
+ fieldInfo.Type != constants.FTYPE_OUTPUT
680
+ },
681
+
682
+ expectNonEphemeral: func() {
683
+ rr := field(self.id)
684
+ rr.isEphemeral = func() { return false }
685
+ return rr
686
+ },
687
+
688
+ /**
689
+ * Use cacheMillis, cacheSeconds, cacheMinutes, cacheHours and cacheDays to not make hidden mistakes in cache time ranges.
690
+ * Sets the cache time.
691
+ *
692
+ * @param time: duration - the cache time from 'times' library.
693
+ */
694
+ setCache: func(time) {
695
+ ll.assert(is_int(time) && time >= times.millisecond,
696
+ "smart.field.setCache: cache time must be an integer >= millisecond. Did you forget to import a standard tengo library 'times'?")
697
+
698
+ tx.cacheSetToField(self.id, time)
699
+ return self
700
+ },
701
+
702
+ /**
703
+ * Sets the cache time in milliseconds.
704
+ *
705
+ * @param millis: number
706
+ */
707
+ setCacheMillis: func(millis) {
708
+ ll.assert(is_int(millis) && millis > 0,
709
+ "smart.field.setCacheMillis: cache time must be a number of milliseconds (> 0)")
710
+ return self.setCache(millis * times.millisecond)
711
+ },
712
+
713
+ /**
714
+ * Sets the cache time in seconds.
715
+ *
716
+ * @param seconds: number
717
+ */
718
+ setCacheSeconds: func(seconds) {
719
+ ll.assert(is_int(seconds) && seconds > 0,
720
+ "smart.field.setCacheSeconds: cache time must be a number of seconds (> 0)")
721
+ return self.setCache(seconds * times.second)
722
+ },
723
+
724
+ /**
725
+ * Sets the cache time in minutes.
726
+ *
727
+ * @param minutes: number
728
+ */
729
+ setCacheMinutes: func(minutes) {
730
+ ll.assert(is_int(minutes) && minutes > 0,
731
+ "smart.field.setCacheMinutes: cache time must be a number of minutes (> 0)")
732
+ return self.setCache(minutes * times.minute)
733
+ },
734
+
735
+ /**
736
+ * Sets the cache time in hours.
737
+ *
738
+ * @param hours: number
739
+ */
740
+ setCacheHours: func(hours) {
741
+ ll.assert(is_int(hours) && hours > 0, "smart.field.setCacheHours: cache time must be a number of hours (> 0)")
742
+ return self.setCache(hours * times.hour)
743
+ },
744
+
745
+ /**
746
+ * Sets the cache time in days
747
+ *
748
+ * @param days: number
749
+ */
750
+ setCacheDays: func(days) {
751
+ ll.assert(is_int(days) && days > 0, "smart.field.setCacheDays: cache time must be a number of days (> 0)")
752
+ return self.setCache(days * 24 * times.hour)
753
+ },
754
+
755
+ /**
756
+ * Returns a smart.Resource object from the resolved field value
757
+ */
758
+ getValue: func() {
759
+ if self.isErrorSet() {
760
+ err := self.getError().getDataAsJson()
761
+ if !is_undefined(err.message) {
762
+ err = err.message
763
+ }
764
+ ll.panic("failed to get the value of field %v, error is not empty: %v", self.name(), err)
765
+ }
766
+
767
+ ll.assert(self.isSet(), "field value is not set; ", self.name())
768
+
769
+ return resource(self.info().Value)
770
+ },
771
+
772
+ /**
773
+ * Returns a smart.Resource object from the resolved field error
774
+ */
775
+ getError: func() {
776
+ return resource(self.info().Error)
777
+ },
778
+
779
+ /**
780
+ * Set (resolve) this field to a Json resource
781
+ *
782
+ * @param value any
783
+ */
784
+ setJson: func(value) {
785
+ self.set(createJsonResource(value))
786
+ },
787
+
788
+ /**
789
+ * Set (resolve) this field to a Map resource
790
+ *
791
+ * @param keyToValue any
792
+ */
793
+ setMap: func(keyToValue) {
794
+ self.set(createMapResource(keyToValue))
795
+ },
796
+
797
+ /**
798
+ * Set (resolve) this field to either another resource or field
799
+ *
800
+ * @param reference smart.resource or smart.field
801
+ */
802
+ set: func(reference) {
803
+ ll.assert(!self.isSet(), "field ", self.name(), " is already set")
804
+
805
+ if isResource(reference) {
806
+ tx.setFieldToResource(self.id, reference.id)
807
+ } else if isField(reference) {
808
+ tx.setFieldToField(self.id, reference.id)
809
+ } else {
810
+ ll.panic("smart resource or smart field expected (target field %v), got %#v: ", self.name(), reference)
811
+ }
812
+ },
813
+
814
+ /**
815
+ * Sets this field to either another resource, field or a JSON value.
816
+ *
817
+ * @param refOrJson: resource|field|JSON
818
+ * @return nothing
819
+ */
820
+ setRefOrJson: func(refOrJson) {
821
+ if is_undefined(refOrJson) {
822
+ ll.panic("attempt to assign undefined value to field %v", self.name())
823
+ }
824
+ self.set(_toRefOrJson(refOrJson))
825
+ },
826
+
827
+ /**
828
+ * Returns a json encoded data of the resolved value resource
829
+ */
830
+ getValueAsJson: func() {
831
+ return self.getValue().getDataAsJson()
832
+ },
833
+
834
+ /**
835
+ * Get future field given field name and field type.
836
+ *
837
+ * @param name: string - field name
838
+ * @param fieldType: field type: input, output
839
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
840
+ * to pass deduplication before resolution.
841
+ *
842
+ * @return field: smart.field
843
+ */
844
+ getFutureField: func(name, fieldType, ...eph) {
845
+ isEph := strictSelf.isEphemeral()
846
+ if len(eph) > 0 {
847
+ isEph = eph[0]
848
+ }
849
+ return _getFutureField(strictSelf, name, fieldType, isEph)
850
+ },
851
+
852
+ /**
853
+ * Get future input field given field name or a path
854
+ *
855
+ * @param nameOrPath: string|string[] - field name or an array representing a path to be traversed
856
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
857
+ * to pass deduplication before resolution.
858
+ *
859
+ * @return field: smart field
860
+ */
861
+ getFutureInputField: func(nameOrPath, ...eph) {
862
+ isEph := strictSelf.isEphemeral()
863
+ if len(eph) > 0 {
864
+ isEph = eph[0]
865
+ }
866
+
867
+ if is_array(nameOrPath) {
868
+ current := strictSelf
869
+ for name in nameOrPath {
870
+ current = current.getFutureField(name, "input", eph...)
871
+ }
872
+ return current
873
+ } else {
874
+ return self.getFutureField(nameOrPath, "input", eph...)
875
+ }
876
+ },
877
+
878
+ /**
879
+ * Get future output field given field name
880
+ *
881
+ * @param name: string - field name
882
+ * @param eph: bool (optional) - when false, the operation becomes pure function, waiting for resource
883
+ * to pass deduplication before resolution.
884
+ *
885
+ * @return field: smart field
886
+ */
887
+ getFutureOutputField: func(name, ...eph) {
888
+ isEph := strictSelf.isEphemeral()
889
+ if len(eph) > 0 {
890
+ isEph = eph[0]
891
+ }
892
+
893
+ if ll.apiVersion() < 1 && !isEph {
894
+ // Dirty hack that saves from issues with buggy pure getField before v1.16.1
895
+ // We can safely drop it when we consider v1.16.0 old enough.
896
+ return self.getFutureField(name, "output", true).expectNonEphemeral()
897
+ }
898
+
899
+ return self.getFutureField(name, "output", isEph)
900
+ }
901
+ }
902
+
903
+ strictSelf = ll.toStrict(self)
904
+
905
+ return strictSelf
906
+ }
907
+
908
+
909
+ /**
910
+ * Universal resource builder.
911
+ *
912
+ * @param r either resource id or resource info
913
+ */
914
+ resourceBuilder = func(r) {
915
+ super := resource(r)
916
+
917
+ self := undefined
918
+
919
+ // Mutator methods of the builder cannot be used after it was built.
920
+ built := false
921
+ assertNotBuilt := func() {
922
+ ll.assert(!built, "builder methods must not be used after a build() was called")
923
+ }
924
+
925
+ self = ll.toStrict(oop.inherit(super, {
926
+ /**
927
+ * Create new field with given name and field type.
928
+ *
929
+ * @param name field name
930
+ * @param fieldType: field type - input, output, dynamic, service, OTW. Use FfTYPE_* constants.
931
+ */
932
+ createField: func(name, fieldType) {
933
+ assertNotBuilt()
934
+ fid := ll.fieldId(super.id, name)
935
+ tx.createField(fid, fieldType)
936
+ return field(fid)
937
+ },
938
+
939
+ /**
940
+ * Create new input field with given name.
941
+ *
942
+ * @param name field name
943
+ */
944
+ createInputField: func(name) {
945
+ assertNotBuilt()
946
+ return self.createField(name, "input")
947
+ },
948
+
949
+ /**
950
+ * Create new output field with given name.
951
+ *
952
+ * @param name field name
953
+ */
954
+ createOutputField: func(name) {
955
+ assertNotBuilt()
956
+ return self.createField(name, "output")
957
+ },
958
+
959
+ /**
960
+ * Lock resource outputs
961
+ */
962
+ lockOutputs: func() {
963
+ assertNotBuilt()
964
+ tx.lockOutputs(super.id)
965
+ },
966
+
967
+ /**
968
+ * Lock resource inputs
969
+ */
970
+ lockInputs: func() {
971
+ assertNotBuilt()
972
+ tx.lockInputs(super.id)
973
+ },
974
+
975
+ /**
976
+ * Lock both resource inputs and outputs
977
+ */
978
+ lock: func() {
979
+ assertNotBuilt()
980
+ self.lockInputs()
981
+ self.lockOutputs()
982
+ },
983
+
984
+ /**
985
+ * Lock inputs and outputs and return resulting smart.Resource
986
+ */
987
+ lockAndBuild: func() {
988
+ self.lock()
989
+ built = true
990
+ return super
991
+ },
992
+
993
+ /**
994
+ * Lock outputs and return resulting smart.Resource
995
+ */
996
+ lockOutputsAndBuild: func() {
997
+ self.lockOutputs()
998
+ built = true
999
+ return super
1000
+ },
1001
+
1002
+ /**
1003
+ * Lock inputs and return resulting smart.Resource
1004
+ */
1005
+ lockInputsAndBuild: func() {
1006
+ self.lockInputs()
1007
+ built = true
1008
+ return super
1009
+ },
1010
+
1011
+ /**
1012
+ * Return smart.Resource without locking inputs and outputs
1013
+ */
1014
+ buildUnlocked: func() {
1015
+ built = true
1016
+ return super
1017
+ }
1018
+ }))
1019
+
1020
+ return self
1021
+ }
1022
+
1023
+ /**
1024
+ * Creates a structural resource and returns a builder from it.
1025
+ *
1026
+ * @params resourceType a resource type
1027
+ * @params data, optional, bytes that will go in Data of the resource.
1028
+ */
1029
+ structBuilder = func(resourceType, ...data) {
1030
+ id := undefined
1031
+ if len(data) > 0 {
1032
+ id = tx.createStruct(resourceType.Name, resourceType.Version, data[0])
1033
+ } else {
1034
+ id = tx.createStruct(resourceType.Name, resourceType.Version)
1035
+ }
318
1036
 
319
- getData: func() {
320
- if self.hasError() {
321
- ll.panic("resource has errors: ", self.info())
1037
+ return resourceBuilder(id)
322
1038
  }
323
- return self.info().Data;
324
- },
325
-
326
-
327
1039
 
328
-
329
- getDataAsJson: func() {
330
- return json.decode(self.getData());
331
- },
332
-
333
-
334
-
335
-
336
- hasError: func() {
337
- return self._hasField("OneTimeWritables", "resourceError")
338
- },
339
-
340
-
341
-
342
-
343
-
344
- hasInputErrors: func() {
345
- return self.info().HasErrors
346
- },
347
-
348
-
349
-
350
-
351
- getError: func() {
352
- if !self.info().HasErrors {
353
- ll.panic("no error set in resource")
1040
+ /**
1041
+ * @param isEph: is ephemeral map
1042
+ */
1043
+ _mapBuilder := func(isEph) {
1044
+ builder := isEph ? ephemeralBuilder(constants.RTYPE_MAP_EPH) : structBuilder(constants.RTYPE_MAP)
1045
+ // ensure no outputs will be created
1046
+ builder.lockOutputs()
1047
+
1048
+ self := undefined
1049
+
1050
+ self = ll.toStrict(oop.inherit(builder, {
1051
+ /**
1052
+ * Adds a reference to the map by a given key.
1053
+ *
1054
+ * @param key: string - how the field will be named in the map
1055
+ * @param value: reference - a field or resource that will be set in the field
1056
+ * @return self: mapBuilder
1057
+ */
1058
+ addRef: func(key, value) {
1059
+ builder.createInputField(key).set(value)
1060
+ return self
1061
+ },
1062
+
1063
+ /**
1064
+ * Adds a primitive value that can be converted to JSON to the map.
1065
+ *
1066
+ * @param key: string - how the field will be named in the map
1067
+ * @param value: any - a primitive value, e.g. Tengo's map.
1068
+ * @return self: mapBuilder
1069
+ */
1070
+ addJson: func(key, value) {
1071
+ builder.createInputField(key).set(createJsonResource(value))
1072
+ return self
1073
+ },
1074
+
1075
+ /**
1076
+ * Adds object automatically converting non-reference objects into Json values.
1077
+ *
1078
+ * @param key: string - how the field will be named in the map
1079
+ * @param value: any - a reference or a primitive value
1080
+ * @return self: mapBuilder
1081
+ */
1082
+ add: func(key, value) {
1083
+ builder.createInputField(key).setRefOrJson(value)
1084
+ return self
1085
+ },
1086
+
1087
+
1088
+ /**
1089
+ * Creates a map in the platforma
1090
+ *
1091
+ * @return nothing
1092
+ */
1093
+ build: func() {
1094
+ return builder.lockAndBuild()
1095
+ }
1096
+ }))
1097
+
1098
+ return self
354
1099
  }
355
- return self.getField("resourceError").getError()
356
- },
357
-
358
- _hasField: func(fieldMapName, fieldName) {
359
- return ll.fromStrict(self.info()[fieldMapName])[fieldName] != undefined
360
- },
361
1100
 
362
-
363
-
364
-
365
-
366
-
367
- hasInputField: func(name) {
368
- return self._hasField("Inputs", name)
369
- },
370
-
371
-
372
-
373
-
374
-
375
-
376
- hasOutputField: func(name) {
377
- return self._hasField("Outputs", name)
378
- },
379
-
380
-
381
-
382
-
383
-
384
-
385
- hasField: func(name) {
386
- return self._hasField("Inputs", name) ||
387
- self._hasField("Outputs", name) ||
388
- self._hasField("DynamicFields", name) ||
389
- self._hasField("OneTimeWritables", name)
390
- },
391
-
392
-
393
- _inputs: undefined,
394
-
395
-
396
-
397
- inputs: func() {
398
- if self._inputs == undefined {
399
- info := self.info()
400
- inputs := maps.mapValues(info.Inputs, field)
401
- if !info.InputsLocked {
402
- return inputs
403
- }
404
- self._inputs = inputs
1101
+ mapBuilder = func() {
1102
+ return _mapBuilder(false)
405
1103
  }
406
- return self._inputs
407
- },
408
1104
 
409
-
410
- _outputs: undefined,
411
-
412
-
413
-
414
- outputs: func() {
415
- if(self._outputs == undefined) {
416
- info := self.info()
417
- outputs := ll.toStrict(maps.mapValues(info.Outputs, field))
418
- if !info.OutputsLocked {
419
- return outputs
420
- }
421
- self._outputs = outputs
1105
+ ephemeralMapBuilder = func() {
1106
+ return _mapBuilder(true)
422
1107
  }
423
- return self._outputs
424
- },
425
-
426
-
427
-
428
-
429
1108
 
1109
+ ephemeralBuilder = func(resourceType, ...data) {
1110
+ id := undefined
1111
+ if (len(data) > 0) {
1112
+ id = tx.createEphemeral(resourceType.Name, resourceType.Version, data[0])
1113
+ } else {
1114
+ id = tx.createEphemeral(resourceType.Name, resourceType.Version)
1115
+ }
430
1116
 
431
- getField: func(name) {
432
- if(!self.hasField(name)) {
433
- ll.panic("field %v is not found", name)
1117
+ return resourceBuilder(id)
434
1118
  }
435
1119
 
436
- return field(ll.fieldId(self.id, name))
437
- },
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
-
447
-
448
-
449
- getFutureField: func(name, fieldType, ...eph) {
450
- isEph := strictSelf.isEphemeral()
451
- if len(eph) > 0 {
452
- isEph = eph[0]
1120
+ createValueResource = func(resourceType, data) {
1121
+ ll.assert(data != undefined, "can't create resource with undefined data")
1122
+ return resource(tx.createValue(resourceType.Name, resourceType.Version, data))
453
1123
  }
454
- return _getFutureField(strictSelf, name, fieldType, isEph)
455
- },
456
-
457
-
458
-
459
-
460
-
461
-
462
1124
 
1125
+ createJsonResource = func(value) {
1126
+ ll.assert(value != undefined, "can't create Json value from undefined")
463
1127
 
1128
+ // important: strict map can't be encoded to JSON
464
1129
 
1130
+ ll.assert(!ll.isStrict(value), "can't encode strict map: ", value)
1131
+ // value = ll.ensureNonStrict(value)
465
1132
 
466
- getFutureInputField: func(nameOrPath, ...eph) {
467
- isEph := strictSelf.isEphemeral()
468
- if len(eph) > 0 {
469
- isEph = eph[0]
470
- }
1133
+ encoded := json.encode(value)
471
1134
 
472
- if is_array(nameOrPath) {
473
- current := strictSelf
474
- for name in nameOrPath {
475
- current = current.getFutureField(name, "input", isEph)
476
- }
477
- return current
478
- } else {
479
- return self.getFutureField(nameOrPath, "input", isEph)
1135
+ return createValueResource(constants.RTYPE_JSON, encoded)
480
1136
  }
481
- },
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
1137
 
1138
+ createMapResourceWithType := func(resourceType, keyToValue) {
1139
+ builder := structBuilder(resourceType)
490
1140
 
1141
+ for key, value in keyToValue {
1142
+ builder.createInputField(key).setRefOrJson(value)
1143
+ }
491
1144
 
492
- getFutureOutputField: func(name, ...eph) {
493
- isEph := strictSelf.isEphemeral()
494
- if len(eph) > 0 {
495
- isEph = eph[0]
496
- }
497
-
498
- if ll.apiVersion() < 1 && !isEph {
499
-
500
-
501
- return self.getFutureField(name, "output", true).expectNonEphemeral()
502
- }
503
-
504
- return self.getFutureField(name, "output", isEph)
505
- },
506
-
507
-
508
-
509
-
510
-
511
-
512
- get: func(name) {
513
- return self.getField(name).getValue()
514
- },
515
-
516
-
517
-
518
-
519
-
520
-
521
-
522
-
523
- kvGet: func(key) {
524
- return tx.resourceKeyValueGet(self.id, key)
525
- },
526
-
527
-
528
-
529
-
530
-
531
-
532
-
533
-
534
- kvSet: func(key, value) {
535
- tx.resourceKeyValueSet(self.id, key, value)
536
- },
537
-
538
-
539
-
540
-
541
-
542
-
543
-
544
-
545
- kvGetAsString: func(key) {
546
- value := tx.resourceKeyValueGet(self.id, key)
547
- if is_undefined(value) {
548
- return undefined
549
- } else {
550
- return string(value)
551
- }
552
- },
553
-
554
-
555
-
556
-
557
-
558
-
559
-
560
-
561
- kvGetAsJson: func(key) {
562
- bytes := self.kvGet(key)
563
- if is_undefined(bytes){
564
- return undefined
565
- } else {
566
- return json.decode(bytes)
567
- }
568
- },
569
-
570
-
571
-
572
-
573
-
574
-
575
-
576
- kvSetJson: func(key, value) {
577
- self.kvSet(key, json.encode(value))
578
- }
579
- }
580
-
581
- strictSelf = ll.toStrict(self)
582
-
583
- return strictSelf
584
- }
585
-
586
- field = func(f) {
587
- self := undefined
588
- strictSelf := undefined
589
-
590
- id := ll.isFieldId(f) ? f : f.ID
591
-
592
- if (is_undefined(id)) {
593
- ll.panic("undefined id in smart.field. <f>="+f)
594
- }
595
-
596
- self = {
597
- _type: "field",
598
-
599
-
600
-
601
-
602
-
603
-
604
-
605
-
606
- id: id,
607
-
608
-
609
-
610
-
611
- name: func() {
612
- return self.id.Name
613
- },
614
-
615
-
616
-
617
-
618
- resource: func() {
619
- return resource(self.id.ResourceID)
620
- },
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
- info: func() {
633
- return tx.getField(self.id)
634
- },
635
-
636
-
637
-
638
-
639
- isSet: func() {
640
- return self.info().IsSet
641
- },
642
-
643
-
644
-
645
-
646
- isValueSet: func() {
647
- return self.info().Value != 0
648
- },
649
-
650
-
651
-
652
-
653
- isErrorSet: func() {
654
- return self.info().Error != 0
655
- },
656
-
657
-
658
-
659
-
660
-
661
-
662
-
663
- isEphemeral: func() {
664
- fieldInfo := self.info()
665
-
666
- if !maps.containsKey(fieldInfo, "Features") {
667
-
668
-
669
-
670
- return true
671
- }
672
-
673
- isParentResourceEphemeral := fieldInfo.Features.ephemeral
674
- if isParentResourceEphemeral {
675
- return true
676
- }
677
-
678
- return fieldInfo.Type != constants.FTYPE_INPUT &&
679
- fieldInfo.Type != constants.FTYPE_OUTPUT
680
- },
681
-
682
- expectNonEphemeral: func() {
683
- rr := field(self.id)
684
- rr.isEphemeral = func() { return false }
685
- return rr
686
- },
687
-
688
-
689
-
690
-
691
-
692
-
693
-
694
- setCache: func(time) {
695
- ll.assert(is_int(time) && time >= times.millisecond,
696
- "smart.field.setCache: cache time must be an integer >= millisecond. Did you forget to import a standard tengo library 'times'?")
697
-
698
- tx.cacheSetToField(self.id, time)
699
- return self
700
- },
701
-
702
-
703
-
704
-
705
-
706
-
707
- setCacheMillis: func(millis) {
708
- ll.assert(is_int(millis) && millis > 0,
709
- "smart.field.setCacheMillis: cache time must be a number of milliseconds (> 0)")
710
- return self.setCache(millis * times.millisecond)
711
- },
712
-
713
-
714
-
715
-
716
-
717
-
718
- setCacheSeconds: func(seconds) {
719
- ll.assert(is_int(seconds) && seconds > 0,
720
- "smart.field.setCacheSeconds: cache time must be a number of seconds (> 0)")
721
- return self.setCache(seconds * times.second)
722
- },
723
-
724
-
725
-
726
-
727
-
728
-
729
- setCacheMinutes: func(minutes) {
730
- ll.assert(is_int(minutes) && minutes > 0,
731
- "smart.field.setCacheMinutes: cache time must be a number of minutes (> 0)")
732
- return self.setCache(minutes * times.minute)
733
- },
734
-
735
-
736
-
737
-
738
-
739
-
740
- setCacheHours: func(hours) {
741
- ll.assert(is_int(hours) && hours > 0, "smart.field.setCacheHours: cache time must be a number of hours (> 0)")
742
- return self.setCache(hours * times.hour)
743
- },
744
-
745
-
746
-
747
-
748
-
749
-
750
- setCacheDays: func(days) {
751
- ll.assert(is_int(days) && days > 0, "smart.field.setCacheDays: cache time must be a number of days (> 0)")
752
- return self.setCache(days * 24 * times.hour)
753
- },
754
-
755
-
756
-
757
-
758
- getValue: func() {
759
- if self.isErrorSet() {
760
- err := self.getError().getDataAsJson()
761
- if !is_undefined(err.message) {
762
- err = err.message
763
- }
764
- ll.panic("failed to get the value of field %v, error is not empty: %v", self.name(), err)
765
- }
766
-
767
- ll.assert(self.isSet(), "field value is not set; ", self.name())
768
-
769
- return resource(self.info().Value)
770
- },
771
-
772
-
773
-
774
-
775
- getError: func() {
776
- return resource(self.info().Error)
777
- },
778
-
779
-
780
-
781
-
782
-
783
-
784
- setJson: func(value) {
785
- self.set(createJsonResource(value))
786
- },
787
-
788
-
789
-
790
-
791
-
792
-
793
- setMap: func(keyToValue) {
794
- self.set(createMapResource(keyToValue))
795
- },
796
-
797
-
798
-
799
-
800
-
801
-
802
- set: func(reference) {
803
- ll.assert(!self.isSet(), "field ", self.name(), " is already set")
804
-
805
- if isResource(reference) {
806
- tx.setFieldToResource(self.id, reference.id)
807
- } else if isField(reference) {
808
- tx.setFieldToField(self.id, reference.id)
809
- } else {
810
- ll.panic("smart resource or smart field expected (target field %v), got %#v: ", self.name(), reference)
811
- }
812
- },
813
-
814
-
815
-
816
-
817
-
818
-
819
-
820
- setRefOrJson: func(refOrJson) {
821
- if is_undefined(refOrJson) {
822
- ll.panic("attempt to assign undefined value to field %v", self.name())
823
- }
824
- self.set(_toRefOrJson(refOrJson))
825
- },
826
-
827
-
828
-
829
-
830
- getValueAsJson: func() {
831
- return self.getValue().getDataAsJson()
832
- },
833
-
834
-
835
-
836
-
837
-
838
-
839
-
840
-
841
-
842
-
843
-
844
- getFutureField: func(name, fieldType, ...eph) {
845
- isEph := strictSelf.isEphemeral()
846
- if len(eph) > 0 {
847
- isEph = eph[0]
848
- }
849
- return _getFutureField(strictSelf, name, fieldType, isEph)
850
- },
851
-
852
-
853
-
854
-
855
-
856
-
857
-
858
-
859
-
860
-
861
- getFutureInputField: func(nameOrPath, ...eph) {
862
- isEph := strictSelf.isEphemeral()
863
- if len(eph) > 0 {
864
- isEph = eph[0]
865
- }
866
-
867
- if is_array(nameOrPath) {
868
- current := strictSelf
869
- for name in nameOrPath {
870
- current = current.getFutureField(name, "input", eph...)
871
- }
872
- return current
873
- } else {
874
- return self.getFutureField(nameOrPath, "input", eph...)
875
- }
876
- },
877
-
878
-
879
-
880
-
881
-
882
-
883
-
884
-
885
-
886
-
887
- getFutureOutputField: func(name, ...eph) {
888
- isEph := strictSelf.isEphemeral()
889
- if len(eph) > 0 {
890
- isEph = eph[0]
891
- }
892
-
893
- if ll.apiVersion() < 1 && !isEph {
894
-
895
-
896
- return self.getFutureField(name, "output", true).expectNonEphemeral()
897
- }
898
-
899
- return self.getFutureField(name, "output", isEph)
900
- }
901
- }
902
-
903
- strictSelf = ll.toStrict(self)
904
-
905
- return strictSelf
906
- }
907
-
908
-
909
-
910
-
911
-
912
-
913
-
914
- resourceBuilder = func(r) {
915
- super := resource(r)
916
-
917
- self := undefined
918
-
919
-
920
- built := false
921
- assertNotBuilt := func() {
922
- ll.assert(!built, "builder methods must not be used after a build() was called")
923
- }
924
-
925
- self = ll.toStrict(oop.inherit(super, {
926
-
927
-
928
-
929
-
930
-
931
-
932
- createField: func(name, fieldType) {
933
- assertNotBuilt()
934
- fid := ll.fieldId(super.id, name)
935
- tx.createField(fid, fieldType)
936
- return field(fid)
937
- },
938
-
939
-
940
-
941
-
942
-
943
-
944
- createInputField: func(name) {
945
- assertNotBuilt()
946
- return self.createField(name, "input")
947
- },
948
-
949
-
950
-
951
-
952
-
953
-
954
- createOutputField: func(name) {
955
- assertNotBuilt()
956
- return self.createField(name, "output")
957
- },
958
-
959
-
960
-
961
-
962
- lockOutputs: func() {
963
- assertNotBuilt()
964
- tx.lockOutputs(super.id)
965
- },
966
-
967
-
968
-
969
-
970
- lockInputs: func() {
971
- assertNotBuilt()
972
- tx.lockInputs(super.id)
973
- },
974
-
975
-
976
-
977
-
978
- lock: func() {
979
- assertNotBuilt()
980
- self.lockInputs()
981
- self.lockOutputs()
982
- },
983
-
984
-
985
-
986
-
987
- lockAndBuild: func() {
988
- self.lock()
989
- built = true
990
- return super
991
- },
992
-
993
-
994
-
995
-
996
- lockOutputsAndBuild: func() {
997
- self.lockOutputs()
998
- built = true
999
- return super
1000
- },
1001
-
1002
-
1003
-
1004
-
1005
- lockInputsAndBuild: func() {
1006
- self.lockInputs()
1007
- built = true
1008
- return super
1009
- },
1010
-
1011
-
1012
-
1013
-
1014
- buildUnlocked: func() {
1015
- built = true
1016
- return super
1017
- }
1018
- }))
1019
-
1020
- return self
1021
- }
1022
-
1023
-
1024
-
1025
-
1026
-
1027
-
1028
-
1029
- structBuilder = func(resourceType, ...data) {
1030
- id := undefined
1031
- if len(data) > 0 {
1032
- id = tx.createStruct(resourceType.Name, resourceType.Version, data[0])
1033
- } else {
1034
- id = tx.createStruct(resourceType.Name, resourceType.Version)
1035
- }
1036
-
1037
- return resourceBuilder(id)
1038
- }
1039
-
1040
-
1041
-
1042
-
1043
- _mapBuilder := func(isEph) {
1044
- builder := isEph ? ephemeralBuilder(constants.RTYPE_MAP_EPH) : structBuilder(constants.RTYPE_MAP)
1045
-
1046
- builder.lockOutputs()
1047
-
1048
- self := undefined
1049
-
1050
- self = ll.toStrict(oop.inherit(builder, {
1051
-
1052
-
1053
-
1054
-
1055
-
1056
-
1057
-
1058
- addRef: func(key, value) {
1059
- builder.createInputField(key).set(value)
1060
- return self
1061
- },
1062
-
1063
-
1064
-
1065
-
1066
-
1067
-
1068
-
1069
-
1070
- addJson: func(key, value) {
1071
- builder.createInputField(key).set(createJsonResource(value))
1072
- return self
1073
- },
1074
-
1075
-
1076
-
1077
-
1078
-
1079
-
1080
-
1081
-
1082
- add: func(key, value) {
1083
- builder.createInputField(key).setRefOrJson(value)
1084
- return self
1085
- },
1086
-
1087
-
1088
-
1089
-
1090
-
1091
-
1092
-
1093
- build: func() {
1094
- return builder.lockAndBuild()
1095
- }
1096
- }))
1097
-
1098
- return self
1099
- }
1100
-
1101
- mapBuilder = func() {
1102
- return _mapBuilder(false)
1103
- }
1104
-
1105
- ephemeralMapBuilder = func() {
1106
- return _mapBuilder(true)
1107
- }
1108
-
1109
- ephemeralBuilder = func(resourceType, ...data) {
1110
- id := undefined
1111
- if (len(data) > 0) {
1112
- id = tx.createEphemeral(resourceType.Name, resourceType.Version, data[0])
1113
- } else {
1114
- id = tx.createEphemeral(resourceType.Name, resourceType.Version)
1115
- }
1116
-
1117
- return resourceBuilder(id)
1118
- }
1119
-
1120
- createValueResource = func(resourceType, data) {
1121
- ll.assert(data != undefined, "can't create resource with undefined data")
1122
- return resource(tx.createValue(resourceType.Name, resourceType.Version, data))
1123
- }
1124
-
1125
- createJsonResource = func(value) {
1126
- ll.assert(value != undefined, "can't create Json value from undefined")
1127
-
1128
-
1129
-
1130
- ll.assert(!ll.isStrict(value), "can't encode strict map: ", value)
1131
-
1132
-
1133
- encoded := json.encode(value)
1134
-
1135
- return createValueResource(constants.RTYPE_JSON, encoded)
1136
- }
1137
-
1138
- createMapResourceWithType := func(resourceType, keyToValue) {
1139
- builder := structBuilder(resourceType)
1140
-
1141
- for key, value in keyToValue {
1142
- builder.createInputField(key).setRefOrJson(value)
1143
- }
1144
-
1145
- return builder.lockAndBuild()
1145
+ return builder.lockAndBuild()
1146
1146
  }
1147
1147
 
1148
1148
  createMapResource = func(keyToValue) {
1149
- return createMapResourceWithType(constants.RTYPE_MAP, keyToValue)
1149
+ return createMapResourceWithType(constants.RTYPE_MAP, keyToValue)
1150
1150
  }
1151
1151
 
1152
1152
  createBinaryMapResource = func(keyToValue) {
1153
- return createMapResourceWithType(constants.RTYPE_BINARY_MAP, keyToValue)
1153
+ return createMapResourceWithType(constants.RTYPE_BINARY_MAP, keyToValue)
1154
1154
  }
1155
1155
 
1156
1156
  createBlobMapResource = func(keyToValue) {
1157
- return createMapResourceWithType(constants.RTYPE_BLOB_MAP, keyToValue)
1157
+ return createMapResourceWithType(constants.RTYPE_BLOB_MAP, keyToValue)
1158
1158
  }
1159
1159
 
1160
1160
  export ll.toStrict({
1161
- isResource: isResource,
1162
- isField: isField,
1163
- isReference: isReference,
1164
- isMapResource: isMapResource,
1165
- isEphMapResource: isEphMapResource,
1166
- isAnyMapResource: isAnyMapResource,
1167
- isLockedEphMapResource: isLockedEphMapResource,
1168
- isJsonResource: isJsonResource,
1169
- resource: resource,
1170
- field: field,
1171
- resourceBuilder: resourceBuilder,
1172
- structBuilder: structBuilder,
1173
- mapBuilder: mapBuilder,
1174
- ephemeralMapBuilder: ephemeralMapBuilder,
1175
- ephemeralBuilder: ephemeralBuilder,
1176
- createValueResource: createValueResource,
1177
- createJsonResource: createJsonResource,
1178
- createMapResourceWithType: createMapResourceWithType,
1179
- createMapResource: createMapResource,
1180
- createBinaryMapResource: createBinaryMapResource,
1181
- createBlobMapResource: createBlobMapResource
1161
+ isResource: isResource,
1162
+ isField: isField,
1163
+ isReference: isReference,
1164
+ isMapResource: isMapResource,
1165
+ isEphMapResource: isEphMapResource,
1166
+ isAnyMapResource: isAnyMapResource,
1167
+ isLockedEphMapResource: isLockedEphMapResource,
1168
+ isJsonResource: isJsonResource,
1169
+ resource: resource,
1170
+ field: field,
1171
+ resourceBuilder: resourceBuilder,
1172
+ structBuilder: structBuilder,
1173
+ mapBuilder: mapBuilder,
1174
+ ephemeralMapBuilder: ephemeralMapBuilder,
1175
+ ephemeralBuilder: ephemeralBuilder,
1176
+ createValueResource: createValueResource,
1177
+ createJsonResource: createJsonResource,
1178
+ createMapResourceWithType: createMapResourceWithType,
1179
+ createMapResource: createMapResource,
1180
+ createBinaryMapResource: createBinaryMapResource,
1181
+ createBlobMapResource: createBlobMapResource
1182
1182
  })