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