@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.
- package/dist/tengo/lib/assets.lib.tengo +52 -52
- package/dist/tengo/lib/assets.swinfo.lib.tengo +45 -45
- package/dist/tengo/lib/constants.lib.tengo +32 -32
- package/dist/tengo/lib/exec.R.internal.lib.tengo +29 -29
- package/dist/tengo/lib/exec.R.lib.tengo +166 -166
- package/dist/tengo/lib/exec.constants.lib.tengo +4 -4
- package/dist/tengo/lib/exec.descriptor.lib.tengo +31 -31
- package/dist/tengo/lib/exec.internal.lib.tengo +297 -297
- package/dist/tengo/lib/exec.lib.tengo +815 -815
- package/dist/tengo/lib/exec.monetization.lib.tengo +130 -130
- package/dist/tengo/lib/exec.package.lib.tengo +212 -212
- package/dist/tengo/lib/exec.python.internal.lib.tengo +6 -6
- package/dist/tengo/lib/exec.python.lib.tengo +146 -146
- package/dist/tengo/lib/exec.runcmd.lib.tengo +371 -371
- package/dist/tengo/lib/exec.runenv.lib.tengo +92 -92
- package/dist/tengo/lib/exec.stream.lib.tengo +40 -40
- package/dist/tengo/lib/file.internal.lib.tengo +77 -77
- package/dist/tengo/lib/file.lib.tengo +57 -57
- package/dist/tengo/lib/ll.lib.tengo +244 -244
- package/dist/tengo/lib/maps.lib.tengo +152 -152
- package/dist/tengo/lib/objects.lib.tengo +13 -13
- package/dist/tengo/lib/oop.lib.tengo +19 -19
- package/dist/tengo/lib/path.lib.tengo +125 -125
- package/dist/tengo/lib/pframes.constants.lib.tengo +28 -28
- package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +33 -33
- package/dist/tengo/lib/pframes.lib.tengo +43 -43
- package/dist/tengo/lib/pframes.ll.lib.tengo +72 -72
- package/dist/tengo/lib/pframes.pcolumn.lib.tengo +49 -49
- package/dist/tengo/lib/pframes.spec.lib.tengo +96 -96
- package/dist/tengo/lib/pframes.util.lib.tengo +288 -288
- package/dist/tengo/lib/pframes.xsv.lib.tengo +272 -272
- package/dist/tengo/lib/render.lib.tengo +75 -75
- package/dist/tengo/lib/sets.lib.tengo +68 -68
- package/dist/tengo/lib/slices.lib.tengo +258 -155
- package/dist/tengo/lib/smart.lib.tengo +1069 -1069
- package/dist/tengo/lib/test.lib.tengo +161 -161
- package/dist/tengo/lib/tpl.lib.tengo +515 -515
- package/dist/tengo/lib/tpl.state-await.lib.tengo +240 -240
- package/dist/tengo/lib/tpl.util.lib.tengo +9 -9
- package/dist/tengo/lib/validation.lib.tengo +378 -378
- package/dist/tengo/lib/workdir.lib.tengo +331 -331
- package/dist/tengo/lib/workdir.proc.lib.tengo +144 -144
- package/dist/tengo/lib/workdir.save.lib.tengo +130 -130
- package/dist/tengo/lib/workflow.bobject.lib.tengo +26 -26
- package/dist/tengo/lib/workflow.bquery.lib.tengo +165 -165
- package/dist/tengo/lib/workflow.constants.lib.tengo +2 -2
- package/dist/tengo/lib/workflow.context.lib.tengo +32 -32
- package/dist/tengo/lib/workflow.lib.tengo +206 -206
- package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
- package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.aggregate.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.map-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
- package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
- package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
- package/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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
204
|
+
// just json if there are no nested resources
|
|
205
|
+
if !_hasReferenceValues(object) {
|
|
206
|
+
return createJsonResource(object)
|
|
207
|
+
}
|
|
213
208
|
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
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
|
-
|
|
432
|
-
if(!self.hasField(name)) {
|
|
433
|
-
ll.panic("field %v is not found", name)
|
|
1117
|
+
return resourceBuilder(id)
|
|
434
1118
|
}
|
|
435
1119
|
|
|
436
|
-
|
|
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
|
-
|
|
467
|
-
isEph := strictSelf.isEphemeral()
|
|
468
|
-
if len(eph) > 0 {
|
|
469
|
-
isEph = eph[0]
|
|
470
|
-
}
|
|
1133
|
+
encoded := json.encode(value)
|
|
471
1134
|
|
|
472
|
-
|
|
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
|
-
|
|
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
|
})
|