@platforma-sdk/workflow-tengo 1.2.6
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/README.md +6 -0
- package/dist/tengo/lib/assets.lib.tengo +64 -0
- package/dist/tengo/lib/constants.lib.tengo +75 -0
- package/dist/tengo/lib/exec.internal.lib.tengo +211 -0
- package/dist/tengo/lib/exec.lib.tengo +516 -0
- package/dist/tengo/lib/exec.package.lib.tengo +296 -0
- package/dist/tengo/lib/exec.runcmd.lib.tengo +275 -0
- package/dist/tengo/lib/exec.runenv.lib.tengo +221 -0
- package/dist/tengo/lib/exec.stream.lib.tengo +64 -0
- package/dist/tengo/lib/file.lib.tengo +112 -0
- package/dist/tengo/lib/ll.lib.tengo +233 -0
- package/dist/tengo/lib/maps.lib.tengo +102 -0
- package/dist/tengo/lib/objects.lib.tengo +22 -0
- package/dist/tengo/lib/oop.lib.tengo +29 -0
- package/dist/tengo/lib/path.lib.tengo +165 -0
- package/dist/tengo/lib/pframes.constants.lib.tengo +53 -0
- package/dist/tengo/lib/pframes.export-pcolumn.lib.tengo +47 -0
- package/dist/tengo/lib/pframes.lib.tengo +84 -0
- package/dist/tengo/lib/pframes.ll.lib.tengo +86 -0
- package/dist/tengo/lib/pframes.util.lib.tengo +244 -0
- package/dist/tengo/lib/pframes.xsv.lib.tengo +178 -0
- package/dist/tengo/lib/render.lib.tengo +100 -0
- package/dist/tengo/lib/sets.lib.tengo +93 -0
- package/dist/tengo/lib/slices.lib.tengo +202 -0
- package/dist/tengo/lib/smart.lib.tengo +1005 -0
- package/dist/tengo/lib/tpl.lib.tengo +535 -0
- package/dist/tengo/lib/tpl.state-await.lib.tengo +255 -0
- package/dist/tengo/lib/tpl.util.lib.tengo +19 -0
- package/dist/tengo/lib/validation.lib.tengo +428 -0
- package/dist/tengo/lib/workdir.lib.tengo +300 -0
- package/dist/tengo/lib/workdir.proc.lib.tengo +158 -0
- package/dist/tengo/lib/workdir.save.lib.tengo +158 -0
- package/dist/tengo/lib/workflow.bobject.lib.tengo +51 -0
- package/dist/tengo/lib/workflow.bquery.lib.tengo +208 -0
- package/dist/tengo/lib/workflow.constants.lib.tengo +8 -0
- package/dist/tengo/lib/workflow.context.lib.tengo +56 -0
- package/dist/tengo/lib/workflow.lib.tengo +298 -0
- package/dist/tengo/tpl/exec.create-python-venv.plj.gz +0 -0
- package/dist/tengo/tpl/exec.exec.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.import-xsv-map.plj.gz +0 -0
- package/dist/tengo/tpl/pframes.map-pframe.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/index.d.ts +4 -0
- package/index.js +13 -0
- package/package.json +25 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
2
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
3
|
+
oop := import("@platforma-sdk/workflow-tengo:oop")
|
|
4
|
+
maps := import("@platforma-sdk/workflow-tengo:maps")
|
|
5
|
+
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
6
|
+
pkg := import("@platforma-sdk/workflow-tengo:exec.package")
|
|
7
|
+
assets := import("@platforma-sdk/workflow-tengo:assets")
|
|
8
|
+
render := import("@platforma-sdk/workflow-tengo:render")
|
|
9
|
+
|
|
10
|
+
_RTYPE_SAVE_RUN_ENV := { Name: "SaveRunEnv/executor", Version: "1" }
|
|
11
|
+
_INPUT_SETTINGS := "settings"
|
|
12
|
+
_INPUT_WD_IN := "workdirIn"
|
|
13
|
+
_INPUT_PACKAGES_MAP := "packagesMap"
|
|
14
|
+
|
|
15
|
+
_OUTPUT_RUN_ENV := "runEnv"
|
|
16
|
+
_OUTPUT_WD_OUT := "workdirOut"
|
|
17
|
+
|
|
18
|
+
saveBuilder := func(wd) {
|
|
19
|
+
self := undefined
|
|
20
|
+
|
|
21
|
+
pathsToSave := []
|
|
22
|
+
envType := ""
|
|
23
|
+
|
|
24
|
+
packagesMap := {}
|
|
25
|
+
|
|
26
|
+
self = {
|
|
27
|
+
addPath: func(path) {
|
|
28
|
+
validation.assertJsonSchema(path, "string", "runenv.saveBuilder.addPath: <path> must be string")
|
|
29
|
+
|
|
30
|
+
pathsToSave = append(pathsToSave, path)
|
|
31
|
+
return self
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
envType: func(type) {
|
|
35
|
+
validation.assertJsonSchema(type, "string", "runenv.saveBuilder.envType: <type> must be string")
|
|
36
|
+
|
|
37
|
+
envType = type
|
|
38
|
+
return self
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Bind package to created run environment, holding it as long as run environment exists.
|
|
43
|
+
*/
|
|
44
|
+
addPackage: func(key, pkgRef) {
|
|
45
|
+
ll.assert(!maps.containsKey(packagesMap, key), "runenv.saveBuilder.addPackage: package %q was already added to SaveRunEnv builder", key)
|
|
46
|
+
validation.assertJsonSchema(pkgRef, validation.reference, "runenv.saveBuilder.addPackage: <pkgRef> must be reference")
|
|
47
|
+
|
|
48
|
+
packagesMap[key] = pkgRef
|
|
49
|
+
return self
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
build: func() {
|
|
53
|
+
ll.assert(envType != "", "runenv.saveBuilder.build: environment type is not set")
|
|
54
|
+
|
|
55
|
+
envSaver := smart.ephemeralBuilder(_RTYPE_SAVE_RUN_ENV)
|
|
56
|
+
|
|
57
|
+
envSaver.getField(_INPUT_WD_IN).set(wd)
|
|
58
|
+
|
|
59
|
+
envSaver.getField(_INPUT_SETTINGS).
|
|
60
|
+
set(smart.createJsonResource({
|
|
61
|
+
paths: pathsToSave,
|
|
62
|
+
type: envType
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
envSaver.getField(_INPUT_PACKAGES_MAP).
|
|
66
|
+
set(smart.createMapResource(packagesMap))
|
|
67
|
+
|
|
68
|
+
return oop.inherit(envSaver.lockAndBuild(), {
|
|
69
|
+
/**
|
|
70
|
+
* Run environment reference
|
|
71
|
+
*
|
|
72
|
+
* @return runEnv: smart.field - field where run environment appears
|
|
73
|
+
* after operation is done
|
|
74
|
+
*/
|
|
75
|
+
runEnv: func() {
|
|
76
|
+
return envSaver.getField(_OUTPUT_RUN_ENV)
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Working directory reference after operation is complete
|
|
81
|
+
*
|
|
82
|
+
* @return wd: smart.field - field where workdir appears after
|
|
83
|
+
* operation is done
|
|
84
|
+
*/
|
|
85
|
+
workdir: func() {
|
|
86
|
+
return envSaver.getField(_OUTPUT_WD_OUT)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return self
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pythonVenvInputSchema := {
|
|
96
|
+
toolset: "string",
|
|
97
|
+
dependencies: {"string": validation.reference},
|
|
98
|
+
// software: validation.reference,
|
|
99
|
+
|
|
100
|
+
pythonDescriptor: pkg.runEnvDescriptorScheme,
|
|
101
|
+
python: validation.reference
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
loadDependencyFiles := func(packageRef, dependencies) {
|
|
105
|
+
if len(dependencies) == 0 {
|
|
106
|
+
return {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
depsMap := {}
|
|
110
|
+
|
|
111
|
+
loader := pkg.assetsLoader(packageRef)
|
|
112
|
+
for depName, fileName in dependencies {
|
|
113
|
+
loader.saveFile(fileName)
|
|
114
|
+
}
|
|
115
|
+
deps := loader.load()
|
|
116
|
+
|
|
117
|
+
for depName, fileName in dependencies {
|
|
118
|
+
depsMap[depName] = deps.getFile(fileName)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return depsMap
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
pythonVenv := func() {
|
|
125
|
+
self := undefined
|
|
126
|
+
|
|
127
|
+
toolset := "pip"
|
|
128
|
+
pythonDescriptor := undefined
|
|
129
|
+
softwareRef := undefined
|
|
130
|
+
dependencies := undefined
|
|
131
|
+
cache := 0
|
|
132
|
+
|
|
133
|
+
self = {
|
|
134
|
+
useToolset: func(name) {
|
|
135
|
+
validation.assertJsonSchema(name, "string",
|
|
136
|
+
"runenv.python.useToolset: <name> must be valid name of python packaging toolset (pip, pipenv and so on)")
|
|
137
|
+
ll.assert(name != "", "runenv.python.useToolset: toolset name must not be empty")
|
|
138
|
+
|
|
139
|
+
toolset = name
|
|
140
|
+
return self
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
runEnvDescriptor: func(descriptor) {
|
|
144
|
+
validation.assertJsonSchema(descriptor, pkg.runEnvDescriptorScheme, "runenv.python.runEnvDescriptor: <descriptor> must be run environment specification")
|
|
145
|
+
ll.assert(descriptor.type == "python", "runenv.python.runEnvDescriptor: not a 'python' run environment: %q", descriptor.type)
|
|
146
|
+
|
|
147
|
+
pythonDescriptor = ll.fromStrict(descriptor)
|
|
148
|
+
return self
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
software: func(pkgRef) {
|
|
152
|
+
validation.assertJsonSchema(pkgRef, validation.reference, "runenv.python.software: <pkgRef> must be a reference to software package")
|
|
153
|
+
|
|
154
|
+
softwareRef = pkgRef
|
|
155
|
+
return self
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
dependencies: func(deps) {
|
|
159
|
+
validation.assertJsonSchema(deps, { "any": "string" }, "runenv.python.dependencies: <deps> must be map of reference file paths")
|
|
160
|
+
|
|
161
|
+
dependencies = deps
|
|
162
|
+
return self
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Sets the cache time.
|
|
167
|
+
*
|
|
168
|
+
* @param time: duration - the cache time from 'times' library.
|
|
169
|
+
*/
|
|
170
|
+
cache: func(time) {
|
|
171
|
+
ll.assert(
|
|
172
|
+
is_int(time),
|
|
173
|
+
"cache time must be an integer. " +
|
|
174
|
+
"Did you forget to import a standard tengo library 'time'?")
|
|
175
|
+
cache = time
|
|
176
|
+
return self
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
build: func() {
|
|
180
|
+
depsMap := loadDependencyFiles(softwareRef, dependencies)
|
|
181
|
+
|
|
182
|
+
archive := pkg.get(pythonDescriptor.registry, pythonDescriptor.package).archive()
|
|
183
|
+
pythonPkg := pkg.install(archive).package()
|
|
184
|
+
|
|
185
|
+
pyVenvInputs := {
|
|
186
|
+
toolset: toolset,
|
|
187
|
+
dependencies: smart.createMapResource(depsMap),
|
|
188
|
+
// software: softwareRef,
|
|
189
|
+
pythonDescriptor: pythonDescriptor,
|
|
190
|
+
python: pythonPkg
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
validation.assertJsonSchema(pyVenvInputs, pythonVenvInputSchema, "incomplete python runenv configuration")
|
|
194
|
+
|
|
195
|
+
tpl := render.create(assets.importTemplate("@platforma-sdk/workflow-tengo:exec.create-python-venv"), pyVenvInputs)
|
|
196
|
+
|
|
197
|
+
self := undefined
|
|
198
|
+
self = ll.toStrict(oop.inherit(tpl, {
|
|
199
|
+
/**
|
|
200
|
+
* Python virtual environment with all dependencies installed.
|
|
201
|
+
*
|
|
202
|
+
* @return venv: smart.field - a reference to the runenv with python venv inside.
|
|
203
|
+
*/
|
|
204
|
+
venv: func() {
|
|
205
|
+
return tpl.output(_OUTPUT_RUN_ENV, cache)
|
|
206
|
+
}
|
|
207
|
+
}))
|
|
208
|
+
|
|
209
|
+
return self
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return self
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export ll.toStrict({
|
|
217
|
+
saveBuilder : saveBuilder,
|
|
218
|
+
|
|
219
|
+
pythonVenv : pythonVenv,
|
|
220
|
+
pythonVenvInputSchema : pythonVenvInputSchema
|
|
221
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A library for working with streams.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
6
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
7
|
+
oop := import("@platforma-sdk/workflow-tengo:oop")
|
|
8
|
+
file := import("@platforma-sdk/workflow-tengo:file")
|
|
9
|
+
|
|
10
|
+
_RTYPE_CREATE_STREAM := { Name: "CreateStream", Version: "2" }
|
|
11
|
+
_RTYPE_STREAM_MANAGER := { Name: "StreamManager", Version: "2" }
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a stream in the specified workdir for the given file path.
|
|
15
|
+
*
|
|
16
|
+
* @param workdir: reference - a reference to the workdir.
|
|
17
|
+
* @param filePath: string - the path of the file in the workdir.
|
|
18
|
+
* @return stream: resource - a smart.resource to the created stream with an output field.
|
|
19
|
+
*/
|
|
20
|
+
_create := func(workdir, filePath) {
|
|
21
|
+
stream := smart.ephemeralBuilder(_RTYPE_CREATE_STREAM)
|
|
22
|
+
stream.getField("workdir").set(workdir)
|
|
23
|
+
stream.getField("filePath").setRefOrJson(filePath)
|
|
24
|
+
|
|
25
|
+
return ll.toStrict(oop.inherit(stream.lockAndBuild(), {
|
|
26
|
+
output: stream.outputs().stream
|
|
27
|
+
}))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a special manager resource that has either a blob or a stream in the `stream` field.
|
|
32
|
+
*
|
|
33
|
+
* @param stream: reference - a reference to the stream.
|
|
34
|
+
* @param downloadableRef: reference - a reference to the downloadable content.
|
|
35
|
+
* @return manager: reference - a reference to the stream manager.
|
|
36
|
+
*/
|
|
37
|
+
_manager := func(stream, downloadableRef) {
|
|
38
|
+
res := smart.structBuilder(_RTYPE_STREAM_MANAGER)
|
|
39
|
+
|
|
40
|
+
res.getField("downloadable").set(downloadableRef)
|
|
41
|
+
res.getField("stream").set(stream.output)
|
|
42
|
+
|
|
43
|
+
return res.lockAndBuild()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a stream and manages its downloadable content.
|
|
48
|
+
*
|
|
49
|
+
* @param workdirBefore: reference - a reference to the workdir before RunCommand were executed.
|
|
50
|
+
* @param workdirAfter: reference - a reference to the resulted workdir field of RunCommand.
|
|
51
|
+
* @param filePath: string - the path of the file in the workdir.
|
|
52
|
+
* @return manager: reference - a reference to the stream manager.
|
|
53
|
+
*/
|
|
54
|
+
create := func(workdirBefore, workdirAfter, filePath) {
|
|
55
|
+
stream := _create(workdirBefore, filePath)
|
|
56
|
+
fileFuture := workdirAfter.files.getFutureInputField(filePath)
|
|
57
|
+
downloadable := file.exportFile(fileFuture)
|
|
58
|
+
|
|
59
|
+
return _manager(stream, downloadable)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export ll.toStrict({
|
|
63
|
+
create: create
|
|
64
|
+
})
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions to work with blobs / files.
|
|
3
|
+
*
|
|
4
|
+
* Allowing to create resources to import files from configured storages
|
|
5
|
+
* as well as creation of resources to facilitate file upload from the
|
|
6
|
+
* users machine.
|
|
7
|
+
*
|
|
8
|
+
* This library exposes rather high level functions working with
|
|
9
|
+
* ImportFileHandle, encoding user intent to import a file from a pl-wide
|
|
10
|
+
* configured storage or to upload file from users side in a universal
|
|
11
|
+
* string representation.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
ll := import("@platforma-sdk/workflow-tengo:ll")
|
|
15
|
+
smart := import("@platforma-sdk/workflow-tengo:smart")
|
|
16
|
+
validation := import("@platforma-sdk/workflow-tengo:validation")
|
|
17
|
+
|
|
18
|
+
json := import("json")
|
|
19
|
+
|
|
20
|
+
//
|
|
21
|
+
// Constants:
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
_RTYPE_BLOB_IMPORT_INTERNAL := { Name: "BlobImportInternal", Version: "1" }
|
|
25
|
+
_RTYPE_BLOB_UPLOAD := { Name: "BlobUpload", Version: "1" }
|
|
26
|
+
_RTYPE_BLOB_DOWNLOAD := { Name: "BlobDownload", Version: "2" }
|
|
27
|
+
|
|
28
|
+
createBlobImportInternal := func(settings) {
|
|
29
|
+
validation.assertJsonSchema(settings, {
|
|
30
|
+
"path": "string",
|
|
31
|
+
"storageId": "string",
|
|
32
|
+
"maxSpeedBytes,omitempty": "number",
|
|
33
|
+
"dataVersion,omitempty": "string"
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
importer := smart.structBuilder(_RTYPE_BLOB_IMPORT_INTERNAL, json.encode(settings)).lockAndBuild()
|
|
37
|
+
|
|
38
|
+
return ll.toStrict({
|
|
39
|
+
file: importer.getField("blob"),
|
|
40
|
+
handle: importer.getField("handle")
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
createBlobUpload := func(settings) {
|
|
45
|
+
validation.assertJsonSchema(settings, {
|
|
46
|
+
"modificationTime": "string",
|
|
47
|
+
"localPath": "string",
|
|
48
|
+
"pathSignature": "string",
|
|
49
|
+
"sizeBytes": "string"
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
uploader := smart.structBuilder(_RTYPE_BLOB_UPLOAD, json.encode(settings)).lockAndBuild()
|
|
53
|
+
|
|
54
|
+
return ll.toStrict({
|
|
55
|
+
file: uploader.getField("blob"),
|
|
56
|
+
handle: uploader.getField("handle")
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Imports an external file into the workflow, either from pre-configured storage,
|
|
62
|
+
* available from the pl instance, or from the client machine via upload.
|
|
63
|
+
*
|
|
64
|
+
* @param importFileHandle string a string file import handle, in most cases obtained via the
|
|
65
|
+
* LS controller in UI
|
|
66
|
+
*
|
|
67
|
+
* @return a structure with two field references
|
|
68
|
+
* {
|
|
69
|
+
* file: field,
|
|
70
|
+
* handle: field
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
importFile := func(importFileHandle) {
|
|
74
|
+
validation.assertJsonSchema(
|
|
75
|
+
importFileHandle,
|
|
76
|
+
`string,regex=^(upload://upload/.*|index://index/.*)`
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
url := ll.parseUrl(importFileHandle)
|
|
80
|
+
path := url.Path[1:] // trim "/"
|
|
81
|
+
|
|
82
|
+
settings := json.decode(path)
|
|
83
|
+
|
|
84
|
+
if url.Scheme == "upload" {
|
|
85
|
+
ll.assert(url.Host == "upload", "unexpected host while parsing import file handle, expected upload found ", url.Host)
|
|
86
|
+
return createBlobUpload(settings)
|
|
87
|
+
} else if url.Scheme == "index" {
|
|
88
|
+
ll.assert(url.Host == "index", "unexpected host while parsing import file handle, expected index found ", url.Host)
|
|
89
|
+
return createBlobImportInternal(settings)
|
|
90
|
+
} else {
|
|
91
|
+
ll.panic("Unknown file import handle schema: ", url.Scheme)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Given a reference to the file, converts it into resource that can be
|
|
97
|
+
* downloaded from the UI side.
|
|
98
|
+
*/
|
|
99
|
+
exportFile := func(blob) {
|
|
100
|
+
builder := smart.structBuilder(_RTYPE_BLOB_DOWNLOAD)
|
|
101
|
+
|
|
102
|
+
builder.createInputField("blob").set(blob)
|
|
103
|
+
|
|
104
|
+
downloader := builder.lockAndBuild()
|
|
105
|
+
|
|
106
|
+
return downloader.getField("downloadable")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export ll.toStrict({
|
|
110
|
+
importFile: importFile,
|
|
111
|
+
exportFile: exportFile
|
|
112
|
+
})
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/* Low level pl API */
|
|
2
|
+
|
|
3
|
+
plapi := import("plapi")
|
|
4
|
+
json := import("json")
|
|
5
|
+
fmt := import("fmt")
|
|
6
|
+
log := import("log")
|
|
7
|
+
text := import("text")
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Prints message to the pl log
|
|
11
|
+
*/
|
|
12
|
+
print := func(txt, ...args) {
|
|
13
|
+
plapi.print(txt, args...)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Panics in a template, prints the error and
|
|
18
|
+
* stops the execution of the template.
|
|
19
|
+
* After that, the controller will set errors to outputs.
|
|
20
|
+
*/
|
|
21
|
+
panic := func(msg, ...args) {
|
|
22
|
+
txt := fmt.sprintf(msg, args...)
|
|
23
|
+
plapi.print(txt)
|
|
24
|
+
|
|
25
|
+
// next statement actually terminates the execution
|
|
26
|
+
plapi.setTemplateError(txt)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Asserts a given condition and panics if it is not met
|
|
31
|
+
*/
|
|
32
|
+
assert := func(condition, msg, ...args) {
|
|
33
|
+
if !condition {
|
|
34
|
+
panic("assertion error: condition failed: "+msg, args...)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks whether the given object is a strict map
|
|
40
|
+
*
|
|
41
|
+
* @param obj any
|
|
42
|
+
* @return strict: bool - whether obj is a 'strict' map
|
|
43
|
+
*/
|
|
44
|
+
isStrict := func(obj) {
|
|
45
|
+
return plapi.isStrictMap(obj)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checks whether the given object is a map, a strict map or an immutable map.
|
|
50
|
+
*
|
|
51
|
+
* @param obj any
|
|
52
|
+
* @return bool whether obj is any kind of map object (map/immutable map/strict map)
|
|
53
|
+
*/
|
|
54
|
+
isMap := func(obj) {
|
|
55
|
+
return is_map(obj) || is_immutable_map(obj) || isStrict(obj)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Converts 'strict' map into a normal one
|
|
60
|
+
*
|
|
61
|
+
* @param r: map|strictMap (will panic if not)
|
|
62
|
+
*
|
|
63
|
+
* @return map: std map
|
|
64
|
+
*/
|
|
65
|
+
fromStrict := func(r) {
|
|
66
|
+
if is_map(r) {
|
|
67
|
+
return r
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if !isStrict(r) {
|
|
71
|
+
panic("not a map", r)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return plapi.strictToMap(r)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* If input is a strict map, converts it to a normal one, otherwise returns the object as is
|
|
79
|
+
*
|
|
80
|
+
* @param r: any object (possible strict map)
|
|
81
|
+
* @return map: tengo object
|
|
82
|
+
*/
|
|
83
|
+
ensureNonStrict := func(r) {
|
|
84
|
+
if isStrict(r) {
|
|
85
|
+
return fromStrict(r)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return r
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Converts map into a 'strict' map, which will panic on
|
|
93
|
+
* access to non-existing elements.
|
|
94
|
+
*
|
|
95
|
+
* @param m: map|strictMap
|
|
96
|
+
*
|
|
97
|
+
* @return strict: strictMap
|
|
98
|
+
*/
|
|
99
|
+
toStrict := func(v) {
|
|
100
|
+
if isStrict(v) {
|
|
101
|
+
return v
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if(!is_map(v)) {
|
|
105
|
+
panic("toStrict(<v>): <v> is not a map", v)
|
|
106
|
+
}
|
|
107
|
+
return plapi.mapToStrict(v)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates a field Id struct from given resource id and field name
|
|
112
|
+
*/
|
|
113
|
+
fieldId := func(resourceId, fieldName) {
|
|
114
|
+
return plapi.newFieldID(resourceId, fieldName)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Returns current template renderer
|
|
119
|
+
*/
|
|
120
|
+
getCurrentTemplateRenderer := func() {
|
|
121
|
+
return plapi.getTemplate()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @return isInit: bool - true when current template is initializing (executed for the first time)
|
|
126
|
+
*/
|
|
127
|
+
isInitializing := func() {
|
|
128
|
+
return plapi.isInit
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get the list of executors available in current Platforma installation.
|
|
133
|
+
*
|
|
134
|
+
* @return executors: []string - list of executors, say ['executor', 'docker', ...]
|
|
135
|
+
*/
|
|
136
|
+
getExecutorsList := func() {
|
|
137
|
+
return plapi.getExecutors()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Joins path elements into a single path.
|
|
142
|
+
*
|
|
143
|
+
* @param elems: ...string - any number of elements.
|
|
144
|
+
*/
|
|
145
|
+
filepathJoin := func(...elems) {
|
|
146
|
+
return plapi.filepathJoin(elems...)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Like filepathJoin, joins elements to a single path,
|
|
151
|
+
* but can be used safely in regexps.
|
|
152
|
+
*
|
|
153
|
+
* @param elems: ...string - any number of elements.
|
|
154
|
+
*/
|
|
155
|
+
filepathRegexpJoin := func(...elems) {
|
|
156
|
+
escapedSeparator := plapi.regexpQuoteMeta(plapi.osSeparator)
|
|
157
|
+
return text.join(elems, escapedSeparator)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Joins elements of the filepath and canonizes it, i.e.
|
|
162
|
+
* semantically resolves ../ and ./ and changes slash
|
|
163
|
+
* to a pathSeparator.
|
|
164
|
+
*
|
|
165
|
+
* @param elems: ...string - any number of elements.
|
|
166
|
+
*/
|
|
167
|
+
filepathCanonize := func(...elem) {
|
|
168
|
+
return plapi.filepathCanonize(elem...)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Returns a path separator. "/" on Unix-like systems, "\\" on Windows.
|
|
173
|
+
*
|
|
174
|
+
* @return separator: string
|
|
175
|
+
*/
|
|
176
|
+
pathSeparator := plapi.osSeparator
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Parses URL and returns a structure containing separe parts of the URL.
|
|
180
|
+
*
|
|
181
|
+
* The general form represented is:
|
|
182
|
+
*
|
|
183
|
+
* [scheme:][//[userinfo@]host][/]path[?query][#fragment]
|
|
184
|
+
*
|
|
185
|
+
* URLs that do not start with a slash after the scheme are interpreted as:
|
|
186
|
+
*
|
|
187
|
+
* scheme:opaque[?query][#fragment]
|
|
188
|
+
*
|
|
189
|
+
* Returns the floowing structure:
|
|
190
|
+
*
|
|
191
|
+
* {
|
|
192
|
+
* Scheme: string,
|
|
193
|
+
* Host: string,
|
|
194
|
+
* Port: number,
|
|
195
|
+
* Path: string,
|
|
196
|
+
* RawPath: string,
|
|
197
|
+
* Query: string
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* @param url: string with the url
|
|
201
|
+
* @return url structure: (see above)
|
|
202
|
+
*/
|
|
203
|
+
parseUrl := func(url) {
|
|
204
|
+
return plapi.urlParse(url)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Returns current execution context, which cab be used is a simple kv-store associated with the renderer instance.
|
|
209
|
+
*/
|
|
210
|
+
getCtx := func() {
|
|
211
|
+
return plapi.getCtx()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export toStrict({
|
|
215
|
+
print: print,
|
|
216
|
+
panic: panic,
|
|
217
|
+
assert: assert,
|
|
218
|
+
isStrict: isStrict,
|
|
219
|
+
isMap: isMap,
|
|
220
|
+
fromStrict: fromStrict,
|
|
221
|
+
ensureNonStrict: ensureNonStrict,
|
|
222
|
+
toStrict: toStrict,
|
|
223
|
+
fieldId: fieldId,
|
|
224
|
+
getCurrentTemplateRenderer: getCurrentTemplateRenderer,
|
|
225
|
+
isInitializing: isInitializing,
|
|
226
|
+
getExecutorsList: getExecutorsList,
|
|
227
|
+
filepathJoin: filepathJoin,
|
|
228
|
+
filepathRegexpJoin: filepathRegexpJoin,
|
|
229
|
+
pathSeparator: pathSeparator,
|
|
230
|
+
filepathCanonize: filepathCanonize,
|
|
231
|
+
parseUrl: parseUrl,
|
|
232
|
+
getCtx: getCtx
|
|
233
|
+
})
|