ts2workflows 0.1.0
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/LICENSE +22 -0
- package/README.md +82 -0
- package/dist/ast/expressions.d.ts +57 -0
- package/dist/ast/expressions.d.ts.map +1 -0
- package/dist/ast/expressions.js +300 -0
- package/dist/ast/stepnames.d.ts +9 -0
- package/dist/ast/stepnames.d.ts.map +1 -0
- package/dist/ast/stepnames.js +268 -0
- package/dist/ast/steps.d.ts +176 -0
- package/dist/ast/steps.d.ts.map +1 -0
- package/dist/ast/steps.js +534 -0
- package/dist/ast/validation.d.ts +20 -0
- package/dist/ast/validation.d.ts.map +1 -0
- package/dist/ast/validation.js +214 -0
- package/dist/ast/workflows.d.ts +28 -0
- package/dist/ast/workflows.d.ts.map +1 -0
- package/dist/ast/workflows.js +74 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +114 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/transpiler/asserts.d.ts +7 -0
- package/dist/transpiler/asserts.d.ts.map +1 -0
- package/dist/transpiler/asserts.js +11 -0
- package/dist/transpiler/expressions.d.ts +6 -0
- package/dist/transpiler/expressions.d.ts.map +1 -0
- package/dist/transpiler/expressions.js +223 -0
- package/dist/transpiler/generated/functionMetadata.d.ts +2 -0
- package/dist/transpiler/generated/functionMetadata.d.ts.map +1 -0
- package/dist/transpiler/generated/functionMetadata.js +324 -0
- package/dist/transpiler/index.d.ts +2 -0
- package/dist/transpiler/index.d.ts.map +1 -0
- package/dist/transpiler/index.js +74 -0
- package/dist/transpiler/statements.d.ts +7 -0
- package/dist/transpiler/statements.d.ts.map +1 -0
- package/dist/transpiler/statements.js +533 -0
- package/dist/transpiler/transformations.d.ts +28 -0
- package/dist/transpiler/transformations.d.ts.map +1 -0
- package/dist/transpiler/transformations.js +461 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +3 -0
- package/language_reference.md +771 -0
- package/package.json +62 -0
- package/types/workflowslib.d.ts +714 -0
|
@@ -0,0 +1,771 @@
|
|
|
1
|
+
# Language reference
|
|
2
|
+
|
|
3
|
+
ts2workflow converts Typescript source code to GCP Workflows YAML syntax. Only a subset of Typescript language features are supported. This page documents supported Typescript features and shows examples of the generted Workflows YAML output.
|
|
4
|
+
|
|
5
|
+
Functions provided by a Javascript runtime (`console.log`, `setInterval`, etc) are not available.
|
|
6
|
+
|
|
7
|
+
Type annotations are allowed. Type checking is done by the compiler but the types dont't affect the generated Workflows code.
|
|
8
|
+
|
|
9
|
+
Semicolon can be used as optional statement delimitter.
|
|
10
|
+
|
|
11
|
+
⚠️ Semantics of certain operations are different from Typescript semantics. These differences are highlighted with the warning icon ⚠️ on this documentation.
|
|
12
|
+
|
|
13
|
+
## Data types
|
|
14
|
+
|
|
15
|
+
- `number`: Integer (64 bit, signed) or double (64 bit, signed floating point number)
|
|
16
|
+
- `string`: `"my beautiful string"`, both single or double quotes are accepted
|
|
17
|
+
- `boolean`: `true`/`false`
|
|
18
|
+
- `bytes`
|
|
19
|
+
- `null`
|
|
20
|
+
- Array: `[1, 2, 3]`
|
|
21
|
+
- Map: `{temperature: -12, unit: "Celsius"}`
|
|
22
|
+
|
|
23
|
+
### Array type
|
|
24
|
+
|
|
25
|
+
⚠️ Arrays are not objects. In particular, methods like `array.map()` and `array.concat()` are not available.
|
|
26
|
+
|
|
27
|
+
⚠️ Accessing out-of-bounds index will cause an IndexError at runtime unlike in Typescript where out-of-bounds access would return `undefined`.
|
|
28
|
+
|
|
29
|
+
### Map type
|
|
30
|
+
|
|
31
|
+
Map keys can be identifiers or strings: `{temperature: -12}` or `{"temperature": -12}`. Trailing commas are allowed.
|
|
32
|
+
|
|
33
|
+
⚠️ Trying to access a non-existing member of an object will throw a KeyError at runtime, unlike in Typescript where it would return `undefined`.
|
|
34
|
+
|
|
35
|
+
### Bytes type
|
|
36
|
+
|
|
37
|
+
It is not possible to construct a bytes object expect by calling a function that returns bytes (e.g. base64.decode). It is not possible to do anything else with a bytes object than to assign it to a variable and to pass it one of the functions that take bytes type as input variable (e.g. base64.encode).
|
|
38
|
+
|
|
39
|
+
### null type
|
|
40
|
+
|
|
41
|
+
In addition to the literal `null`, the Typescript `undefined` value is also treated as `null` in Workflows YAML.
|
|
42
|
+
|
|
43
|
+
## Expressions
|
|
44
|
+
|
|
45
|
+
Most Typescript expressions work as expected.
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
a + b
|
|
51
|
+
|
|
52
|
+
args.users[3].id
|
|
53
|
+
|
|
54
|
+
name === 'Bean'
|
|
55
|
+
|
|
56
|
+
sys.get_env('GOOGLE_CLOUD_PROJECT_ID')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Operators:
|
|
60
|
+
|
|
61
|
+
| Operator | Description |
|
|
62
|
+
| ------------ | -------------------------------------------- |
|
|
63
|
+
| + | arithmetic addition and string concatenation |
|
|
64
|
+
| - | arithmetic subtraction or unary negation |
|
|
65
|
+
| \* | multiplication |
|
|
66
|
+
| / | float division |
|
|
67
|
+
| % | remainder division |
|
|
68
|
+
| ==, === | equal to (both mean strict equality) |
|
|
69
|
+
| !=, !== | not equal to (both mean strict equality) |
|
|
70
|
+
| <, >, <=, >= | inequality comparisons |
|
|
71
|
+
| &&, \|\|, ! | logical operators |
|
|
72
|
+
| in | check if a property is present in an object |
|
|
73
|
+
| ?? | nullish coalescing |
|
|
74
|
+
|
|
75
|
+
The [precendence order of operators](https://cloud.google.com/workflows/docs/reference/syntax/datatypes#order-operations) is the same as in GCP Workflows.
|
|
76
|
+
|
|
77
|
+
See [expression in GCP Workflows](https://cloud.google.com/workflows/docs/reference/syntax/expressions) for more information.
|
|
78
|
+
|
|
79
|
+
## Template literals
|
|
80
|
+
|
|
81
|
+
Template literals are strings that support string interpolation. For example, `Hello ${name}`.
|
|
82
|
+
|
|
83
|
+
⚠️ Interpolated values can (only) be numbers, strings or booleans. Other types will throw a TypeError at runtime.
|
|
84
|
+
|
|
85
|
+
## Subworkflow definitions
|
|
86
|
+
|
|
87
|
+
Typescript `function`s are converted to subworkflow definitions.
|
|
88
|
+
|
|
89
|
+
The program code must be written inside workflow blocks. Only `function` and `import` declarations are allowed on the top level of a source code file. Functions can only be defined at the top level of a source file, not inside functions or in other nested scopes.
|
|
90
|
+
|
|
91
|
+
The workflow execution starts from the subworkflow called "main".
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
function main(): void {
|
|
95
|
+
const a = 1
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function anotherWorkflow(): number {
|
|
99
|
+
const b = 10
|
|
100
|
+
return 2 * b
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Workflows can have parameters:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
function multiply(firstFactor: number, secondFactor: number): number {
|
|
108
|
+
return firstFactor * secondFactor
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Parameters can be optional and have a default value that is used if a value is not provided in a subworkflow call:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
function log(x, base = 10) {
|
|
116
|
+
return 'Should compute the logarithm of x'
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Returning value from a subworkflow
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
return 'Success'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The returned value can be an expression:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
return firstFactor * secondFactor
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Calling functions and subworkflows
|
|
133
|
+
|
|
134
|
+
The statement
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const projectId = sys.get_env('GOOGLE_CLOUD_PROJECT_ID')
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
is converted to an [assign step](https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step):
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
- assign1:
|
|
144
|
+
assign:
|
|
145
|
+
- projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This syntax can be used to call [standard library functions](https://cloud.google.com/workflows/docs/reference/stdlib/overview), subworkflows or connectors.
|
|
149
|
+
|
|
150
|
+
GCP Workflows language has two ways of calling functions and subworkflows: as expression in an [assign step](https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step) or as [call step](https://cloud.google.com/workflows/docs/reference/syntax/calls). They can mostly be used interchangeably. However, [blocking calls](https://cloud.google.com/workflows/docs/reference/syntax/expressions#blocking-calls) must be made as call steps. The transpiler tries to automatically output a call step when necessary.
|
|
151
|
+
|
|
152
|
+
It is also possible to force a function to be called as call step. This might be useful, if the transpiler fails to output call step when it should, or if you want to use named parameters. For example, the following Typescript program
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { call_step, sys } from 'ts2workflows/types/workflowslib'
|
|
156
|
+
|
|
157
|
+
function main() {
|
|
158
|
+
call_step(sys.log, {
|
|
159
|
+
json: { message: 'Hello log' },
|
|
160
|
+
severity: 'INFO',
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
is converted to call step:
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
main:
|
|
169
|
+
steps:
|
|
170
|
+
- call1:
|
|
171
|
+
call: sys.log
|
|
172
|
+
args:
|
|
173
|
+
json:
|
|
174
|
+
message: Hello log
|
|
175
|
+
severity: INFO
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Some Workflows standard library functions have names that are reserved keywords in Typescript. Those functions must be called with alternative names in ts2workflows source code:
|
|
179
|
+
|
|
180
|
+
- To generate a call to `default()` in Workflows code, use the nullish coalescing operator `??`.
|
|
181
|
+
- To generete a call to `if()` in Workflows code, use the ternary operator `a ? b : c`.
|
|
182
|
+
|
|
183
|
+
## Assignments
|
|
184
|
+
|
|
185
|
+
The statement
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const name = 'Bean'
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
is converted to an [assign step](https://cloud.google.com/workflows/docs/reference/syntax/variables#assign-step):
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
- assign1:
|
|
195
|
+
assign:
|
|
196
|
+
- name: Bean
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Compound assignments are also supported:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
total += 1
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
is converted to
|
|
206
|
+
|
|
207
|
+
```yaml
|
|
208
|
+
- assign1:
|
|
209
|
+
assign:
|
|
210
|
+
- total: ${total + 1}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Conditional statements
|
|
214
|
+
|
|
215
|
+
The statement
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
if (hour < 12) {
|
|
219
|
+
part_of_the_day = 'morning'
|
|
220
|
+
} else if (hour < 17) {
|
|
221
|
+
part_of_the_day = 'afternoon'
|
|
222
|
+
} else if (hour < 21) {
|
|
223
|
+
part_of_the_day = 'evening'
|
|
224
|
+
} else {
|
|
225
|
+
part_of_the_day = 'night'
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
is converted to a [switch step](https://cloud.google.com/workflows/docs/reference/syntax/conditions)
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
- switch1:
|
|
233
|
+
switch:
|
|
234
|
+
- condition: ${hour < 12}
|
|
235
|
+
steps:
|
|
236
|
+
- assign1:
|
|
237
|
+
assign:
|
|
238
|
+
- part_of_the_day: morning
|
|
239
|
+
- condition: ${hour < 17}
|
|
240
|
+
steps:
|
|
241
|
+
- assign2:
|
|
242
|
+
assign:
|
|
243
|
+
- part_of_the_day: afternoon
|
|
244
|
+
- condition: ${hour < 21}
|
|
245
|
+
steps:
|
|
246
|
+
- assign3:
|
|
247
|
+
assign:
|
|
248
|
+
- part_of_the_day: evening
|
|
249
|
+
- condition: true
|
|
250
|
+
steps:
|
|
251
|
+
- assign4:
|
|
252
|
+
assign:
|
|
253
|
+
- part_of_the_day: night
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Switch statements
|
|
257
|
+
|
|
258
|
+
Typescript switch statements are transpiled to chains of conditions. For example, this statement
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
let b
|
|
262
|
+
switch (a) {
|
|
263
|
+
case 1:
|
|
264
|
+
b = 'first'
|
|
265
|
+
break
|
|
266
|
+
|
|
267
|
+
case 2:
|
|
268
|
+
b = 'second'
|
|
269
|
+
break
|
|
270
|
+
|
|
271
|
+
default:
|
|
272
|
+
b = 'other'
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return b
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
is converted to
|
|
279
|
+
|
|
280
|
+
```yaml
|
|
281
|
+
steps:
|
|
282
|
+
- assign1:
|
|
283
|
+
assign:
|
|
284
|
+
- b: null
|
|
285
|
+
- switch1:
|
|
286
|
+
switch:
|
|
287
|
+
- condition: ${a == 1}
|
|
288
|
+
next: assign2
|
|
289
|
+
- condition: ${a == 2}
|
|
290
|
+
next: assign3
|
|
291
|
+
- condition: true
|
|
292
|
+
next: assign4
|
|
293
|
+
- assign2:
|
|
294
|
+
assign:
|
|
295
|
+
- b: first
|
|
296
|
+
- next1:
|
|
297
|
+
next: return1
|
|
298
|
+
- assign3:
|
|
299
|
+
assign:
|
|
300
|
+
- b: second
|
|
301
|
+
- next2:
|
|
302
|
+
next: return1
|
|
303
|
+
- assign4:
|
|
304
|
+
assign:
|
|
305
|
+
- b: other
|
|
306
|
+
- return1:
|
|
307
|
+
return: ${b}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Conditional (ternary) operator
|
|
311
|
+
|
|
312
|
+
The expression
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
x > 0 ? 'positive' : 'not positive'
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
is converted to an [if() expression](https://cloud.google.com/workflows/docs/reference/stdlib/expression-helpers#conditional_functions):
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
${if(x > 0, "positive", "not positive")}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
⚠️ Note that Workflows always evaluates both expression branches unlike Typescript which evaluates only the branch that gets executed.
|
|
325
|
+
|
|
326
|
+
## Nullish coalescing operator
|
|
327
|
+
|
|
328
|
+
The expression
|
|
329
|
+
|
|
330
|
+
```javascript
|
|
331
|
+
x ?? 'default value'
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
is converted to an [default() expression](https://cloud.google.com/workflows/docs/reference/stdlib/expression-helpers#conditional_functions):
|
|
335
|
+
|
|
336
|
+
```yaml
|
|
337
|
+
${default(x, "default value")}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
⚠️ Note that Workflows always evaluates the right-hand side expression unlike Typescript which evaluates the right-hand side only if the left-hand side is `null` or `undefined`.
|
|
341
|
+
|
|
342
|
+
## Loops
|
|
343
|
+
|
|
344
|
+
The fragment
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
let total = 0
|
|
348
|
+
for (const i of [1, 2, 3]) {
|
|
349
|
+
total += i
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
is converted to the following [for loop statement](https://cloud.google.com/workflows/docs/reference/syntax/iteration)
|
|
354
|
+
|
|
355
|
+
```yaml
|
|
356
|
+
steps:
|
|
357
|
+
- assign1:
|
|
358
|
+
assign:
|
|
359
|
+
- total: 0
|
|
360
|
+
- for1:
|
|
361
|
+
for:
|
|
362
|
+
value: i
|
|
363
|
+
in:
|
|
364
|
+
- 1
|
|
365
|
+
- 2
|
|
366
|
+
- 3
|
|
367
|
+
steps:
|
|
368
|
+
- assign2:
|
|
369
|
+
assign:
|
|
370
|
+
- total: ${total + i}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`while` and `do...while` loops are also supported:
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
const total = 0
|
|
377
|
+
let i = 5
|
|
378
|
+
|
|
379
|
+
while (i > 0) {
|
|
380
|
+
total += i
|
|
381
|
+
i -= 1
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
let k = 5
|
|
385
|
+
do {
|
|
386
|
+
total += k
|
|
387
|
+
k -= 1
|
|
388
|
+
} while (k > 0)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
`for...in` loops are not supported.
|
|
392
|
+
|
|
393
|
+
### Break and continue in loops
|
|
394
|
+
|
|
395
|
+
Breaking out of a loop:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
let total = 0
|
|
399
|
+
for (const i of [1, 2, 3, 4]) {
|
|
400
|
+
if (total > 5) {
|
|
401
|
+
break
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
total += i
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Continuing from the next iteration of a loop:
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
let total = 0
|
|
412
|
+
for (i of [1, 2, 3, 4]) {
|
|
413
|
+
if (i % 2 == 0) {
|
|
414
|
+
continue
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
total += i
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Parallel branches
|
|
422
|
+
|
|
423
|
+
The special function `parallel` can be used to execute several code branches in parallel. The code blocks to be executed in parallel are given as an array of `() => void` functions. For example, the following code
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
parallel([
|
|
427
|
+
() => {
|
|
428
|
+
http.post('https://forum.dreamland.test/register/bean')
|
|
429
|
+
},
|
|
430
|
+
() => {
|
|
431
|
+
http.post('https://forum.dreamland.test/register/elfo')
|
|
432
|
+
},
|
|
433
|
+
() => {
|
|
434
|
+
http.post('https://forum.dreamland.test/register/luci')
|
|
435
|
+
},
|
|
436
|
+
])
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
is converted to [parallel steps](https://cloud.google.com/workflows/docs/reference/syntax/parallel-steps)
|
|
440
|
+
|
|
441
|
+
```yaml
|
|
442
|
+
- parallel1:
|
|
443
|
+
parallel:
|
|
444
|
+
branches:
|
|
445
|
+
- branch1:
|
|
446
|
+
steps:
|
|
447
|
+
- call1:
|
|
448
|
+
call: http.post
|
|
449
|
+
args:
|
|
450
|
+
url: https://forum.dreamland.test/register/bean
|
|
451
|
+
- branch2:
|
|
452
|
+
steps:
|
|
453
|
+
- call2:
|
|
454
|
+
call: http.post
|
|
455
|
+
args:
|
|
456
|
+
url: https://forum.dreamland.test/register/elfo
|
|
457
|
+
- branch3:
|
|
458
|
+
steps:
|
|
459
|
+
- call3:
|
|
460
|
+
call: http.post
|
|
461
|
+
args:
|
|
462
|
+
url: https://forum.dreamland.test/register/luci
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The branches can also be subworkflow names:
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
parallel([my_subworkflow1, my_subworkflow2, my_subworklfow3])
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
An optional second parameter is an object that can define shared variables and concurrency limits:
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
let numPosts = 0
|
|
475
|
+
|
|
476
|
+
parallel(
|
|
477
|
+
[
|
|
478
|
+
() => {
|
|
479
|
+
n = http.get('https://forum.dreamland.test/numPosts/bean')
|
|
480
|
+
numPosts = numPosts + n
|
|
481
|
+
},
|
|
482
|
+
() => {
|
|
483
|
+
n = http.get('https://forum.dreamland.test/numPosts/elfo')
|
|
484
|
+
numPosts = numPosts + n
|
|
485
|
+
},
|
|
486
|
+
],
|
|
487
|
+
{
|
|
488
|
+
shared: ['numPosts'],
|
|
489
|
+
concurrency_limit: 2,
|
|
490
|
+
},
|
|
491
|
+
)
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## Parallel iteration
|
|
495
|
+
|
|
496
|
+
The following form of `parallel` can be called to execute iterations in parallel. The only statement in the function body must be a `for` loop.
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
parallel(() => {
|
|
500
|
+
for (const username of ['bean', 'elfo', 'luci']) {
|
|
501
|
+
http.post('https://forum.dreamland.test/register/' + username)
|
|
502
|
+
}
|
|
503
|
+
})
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
is converted to [parallel iteration](https://cloud.google.com/workflows/docs/reference/syntax/parallel-steps#parallel-iteration):
|
|
507
|
+
|
|
508
|
+
```yaml
|
|
509
|
+
- parallel1:
|
|
510
|
+
parallel:
|
|
511
|
+
for:
|
|
512
|
+
value: username
|
|
513
|
+
in:
|
|
514
|
+
- bean
|
|
515
|
+
- elfo
|
|
516
|
+
- luci
|
|
517
|
+
steps:
|
|
518
|
+
- call1:
|
|
519
|
+
call: http.post
|
|
520
|
+
args:
|
|
521
|
+
url: ${"https://forum.dreamland.test/register/" + username}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
The shared variables and concurrency limits can be set with the following syntax:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
let total = 0
|
|
528
|
+
|
|
529
|
+
parallel(
|
|
530
|
+
() => {
|
|
531
|
+
for (const i of [1, 2, 3, 4]) {
|
|
532
|
+
total += i
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
shared: ['total'],
|
|
537
|
+
concurrency_limit: 2,
|
|
538
|
+
},
|
|
539
|
+
)
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## Try/catch statements
|
|
543
|
+
|
|
544
|
+
The statement
|
|
545
|
+
|
|
546
|
+
```javascript
|
|
547
|
+
try {
|
|
548
|
+
http.get('https://visit.dreamland.test/')
|
|
549
|
+
} catch (err) {
|
|
550
|
+
return 'Error!'
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
is compiled to the following [try/except structure](https://cloud.google.com/workflows/docs/reference/syntax/catching-errors)
|
|
555
|
+
|
|
556
|
+
```yaml
|
|
557
|
+
- try1:
|
|
558
|
+
try:
|
|
559
|
+
steps:
|
|
560
|
+
- call1:
|
|
561
|
+
call: http.get
|
|
562
|
+
args:
|
|
563
|
+
url: https://visit.dreamland.test/
|
|
564
|
+
except:
|
|
565
|
+
as: err
|
|
566
|
+
steps:
|
|
567
|
+
- return1:
|
|
568
|
+
return: Error!
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
The error variable and other variables created inside the catch block are accessible only in that block's scope (similar to [the variable scoping in Workflows](https://cloud.google.com/workflows/docs/reference/syntax/catching-errors#variable-scope)).
|
|
572
|
+
|
|
573
|
+
## Retrying on errors
|
|
574
|
+
|
|
575
|
+
It is possible to set a retry policy for a try-catch statement. Because Typescript does not have `retry` keyword, the retry is implemented by a special `retry_policy` function. It must be called immediately after a try-catch block. A call to the `retry_policy` is ignored elsewhere.
|
|
576
|
+
|
|
577
|
+
The `retry_policy` function must be called with parameters defining a retry policy. It can be either a policy provided by GCP Workflows or a custom retry policy. See the GCP documentation for the [required parameters for the two policy types](https://cloud.google.com/workflows/docs/reference/syntax/retrying#try-retry).
|
|
578
|
+
|
|
579
|
+
A sample with a GCP-provided retry policy:
|
|
580
|
+
|
|
581
|
+
```javascript
|
|
582
|
+
import { http, retry_policy } from 'ts2workflows/types/workflowslib'
|
|
583
|
+
|
|
584
|
+
function main() {
|
|
585
|
+
try {
|
|
586
|
+
http.get('https://visit.dreamland.test/')
|
|
587
|
+
} catch (err) {
|
|
588
|
+
return 'Error!'
|
|
589
|
+
}
|
|
590
|
+
retry_policy({ policy: http.default_retry })
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
A sample with a custom retry policy:
|
|
595
|
+
|
|
596
|
+
```javascript
|
|
597
|
+
import { http, retry_policy } from 'ts2workflows/types/workflowslib'
|
|
598
|
+
|
|
599
|
+
function main() {
|
|
600
|
+
try {
|
|
601
|
+
http.get('https://visit.dreamland.test/')
|
|
602
|
+
} catch (err) {
|
|
603
|
+
return 'Error!'
|
|
604
|
+
}
|
|
605
|
+
retry_policy({
|
|
606
|
+
predicate: http.default_retry_predicate,
|
|
607
|
+
max_retries: 3,
|
|
608
|
+
backoff: {
|
|
609
|
+
initial_delay: 0.5,
|
|
610
|
+
max_delay: 60,
|
|
611
|
+
multiplier: 2,
|
|
612
|
+
},
|
|
613
|
+
})
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
The above is compiled to the following [try/except structure](https://cloud.google.com/workflows/docs/reference/syntax/catching-errors)
|
|
618
|
+
|
|
619
|
+
```yaml
|
|
620
|
+
main:
|
|
621
|
+
steps:
|
|
622
|
+
- try1:
|
|
623
|
+
try:
|
|
624
|
+
steps:
|
|
625
|
+
- call1:
|
|
626
|
+
call: http.get
|
|
627
|
+
args:
|
|
628
|
+
url: https://visit.dreamland.test/
|
|
629
|
+
retry:
|
|
630
|
+
predicate: ${http.default_retry_predicate}
|
|
631
|
+
max_retries: 3
|
|
632
|
+
backoff:
|
|
633
|
+
initial_delay: 0.5
|
|
634
|
+
max_delay: 60
|
|
635
|
+
multiplier: 2
|
|
636
|
+
except:
|
|
637
|
+
as: err
|
|
638
|
+
steps:
|
|
639
|
+
- return1:
|
|
640
|
+
return: Error!
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
## Throwing errors
|
|
644
|
+
|
|
645
|
+
The statement
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
throw 'Error!'
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
is compiled to the following [raise block](https://cloud.google.com/workflows/docs/reference/syntax/raising-errors)
|
|
652
|
+
|
|
653
|
+
```yaml
|
|
654
|
+
- raise1:
|
|
655
|
+
raise: 'Error!'
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
The error can be a string, a map or an expression that evaluates to string or map.
|
|
659
|
+
|
|
660
|
+
Thrown errors can be handled by a try statement.
|
|
661
|
+
|
|
662
|
+
## Labeled steps
|
|
663
|
+
|
|
664
|
+
The transpiler labels output steps with the step type and sequential numbering by default: e.g. `assign1`, `assign2`, etc. The automatic labels can be overridden by using Typescript labeled statements.
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
setName: const name = 'Bean'
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
is converted to a step with the label `setName`:
|
|
671
|
+
|
|
672
|
+
```yaml
|
|
673
|
+
- setName:
|
|
674
|
+
assign:
|
|
675
|
+
- name: Bean
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
## Type annotations for standard library functions
|
|
679
|
+
|
|
680
|
+
Type annotations for GCP Workflows standard library functions and expression helpers are provided by importing "ts2workflows/types/workflowslib".
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import { sys } from 'ts2workflows/types/workflowslib'
|
|
684
|
+
|
|
685
|
+
function read_from_env() {
|
|
686
|
+
return sys.get_env('GOOGLE_CLOUD_PROJECT_ID')
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
At the moment, type annotations are provided for some [connectors](https://cloud.google.com/workflows/docs/reference/googleapis) but not for all of them.
|
|
691
|
+
|
|
692
|
+
## Special run-time functions
|
|
693
|
+
|
|
694
|
+
ts2workflows provides some special functions for implementing features that are not directly supported by Typescript language features. The type annotations for these functions can be imported from ts2workflows/types/workflowslib:
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
import {
|
|
698
|
+
call_step,
|
|
699
|
+
parallel,
|
|
700
|
+
retry_policy,
|
|
701
|
+
} from 'ts2workflows/types/workflowslib'
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### call_step
|
|
705
|
+
|
|
706
|
+
```typescript
|
|
707
|
+
function call_step(func: Function, args: Record<string, unknown>): unknown
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
The `call_step` function outputs a [call step](https://cloud.google.com/workflows/docs/reference/syntax/calls).
|
|
711
|
+
|
|
712
|
+
### parallel
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
function parallel(
|
|
716
|
+
branches: (() => void)[] | (() => void),
|
|
717
|
+
options?: {
|
|
718
|
+
shared?: string[]
|
|
719
|
+
concurrency_limit?: number
|
|
720
|
+
exception_policy?: string
|
|
721
|
+
},
|
|
722
|
+
): void
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
The `parallel` function executes code blocks in parallel (using [parallel step](https://cloud.google.com/workflows/docs/reference/syntax/parallel-steps)). See the previous sections covering parallel branches and iteration.
|
|
726
|
+
|
|
727
|
+
### retry_policy
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
function retry_policy(
|
|
731
|
+
params:
|
|
732
|
+
| {
|
|
733
|
+
policy: (exception: unknown) => void
|
|
734
|
+
}
|
|
735
|
+
| {
|
|
736
|
+
predicate: (exception: unknown) => boolean
|
|
737
|
+
max_retries: number
|
|
738
|
+
backoff: {
|
|
739
|
+
initial_delay: number
|
|
740
|
+
max_delay: number
|
|
741
|
+
multiplier: number
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
): void
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
The `retry_policy` function can called right after a `try`-`catch` block to specify a retry policy. See the section on retrying.
|
|
748
|
+
|
|
749
|
+
## Source code comments
|
|
750
|
+
|
|
751
|
+
In-line comments start with `//`. The rest of the line starting from `//` is considered a comment. Multiline comments are defined with the `/* */` syntax.
|
|
752
|
+
|
|
753
|
+
Example:
|
|
754
|
+
|
|
755
|
+
```typescript
|
|
756
|
+
const var1 = 1 // This is a comment
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
## Unsupported Typescript features
|
|
760
|
+
|
|
761
|
+
ts2workflows supports only a subset of all Typescript language features. Some examples that are not (yet) supported by ts2workflows:
|
|
762
|
+
|
|
763
|
+
- Functions provided by a Javascript runtime (`console.log`, `setInterval`, etc) are not available. Only the [GCP Workflows standard library functions](https://cloud.google.com/workflows/docs/reference/stdlib/overview) and [connectors](https://cloud.google.com/workflows/docs/reference/googleapis) are available.
|
|
764
|
+
- Classes (`class`) are not supported
|
|
765
|
+
- Arrays and maps are not objects. In particular, arrays don't have methods such as `array.push()`, `array.map()`, etc.
|
|
766
|
+
- Functions (subworkflows) are not first-class objects. Functions can not be assigned to a variable or passed to other functions
|
|
767
|
+
- Update expressions (`x++` and similar) are not supported
|
|
768
|
+
- Destructuring (`[a, b] = func()`) is not supported
|
|
769
|
+
- and many other Typescript language features are not supported
|
|
770
|
+
|
|
771
|
+
Some of these might be implemented later, but the goal of ts2workflows project is not to implement the full Typescript compatibility.
|