Skip to content

Commit 8aa8b14

Browse files
committed
$call instruction to call functions
1 parent f66c136 commit 8aa8b14

File tree

7 files changed

+293
-1
lines changed

7 files changed

+293
-1
lines changed

LANGUAGE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,65 @@ This instruction can also be used to create asynchronous value from synchronous
346346
In the example above a hypothetical logger logs message when asynchronous value is resolved. `$delay` instruction result is an asynchrnous value that resolves 1 second after its evaluation with the value `"test"`.
347347

348348
`$wait` keyword is optional, the default is 0. It means that the interpreter should schedule the script evaluation as soon as possible but do not execute it immediately.
349+
350+
351+
## Defining and calling functions with `$func` and `$call`
352+
353+
Anonymous or named function can be defined in the script to be passed to executors (either predefined or supplied by user) or simply to be used multiple times.
354+
355+
```json
356+
[
357+
{
358+
"$func": {
359+
"$exec": "router",
360+
"$method": "get",
361+
"$args": { "path": { "$data": "/path" } }
362+
},
363+
"$name": "getRes"
364+
"$args": [ "path" ]
365+
},
366+
{
367+
"$call": "getRes",
368+
"$args": [ "/resource/1" ]
369+
},
370+
{
371+
"$call": { "$ref": "/0" },
372+
"$args": { "path": "/resource/2" }
373+
},
374+
{
375+
"$call": { "$ref": "1/0" },
376+
"$args": "/resource/3"
377+
}
378+
]
379+
```
380+
381+
In the example above the same function `getRes` is used three times, being called by name and using $ref with absolute and relative JSON-pointers. Arguments can be passed to function as array, as an object (property names should match parameters declarations, otherwise an exception will be thrown) and as a scalar value if there is only one parameter.
382+
383+
Functions can be used as parameters in the executors:
384+
385+
```json
386+
{
387+
"$exec": "array",
388+
"$method": "map",
389+
"$args": {
390+
"data": [
391+
"/resource/1",
392+
"/resource/2",
393+
"/resource/3"
394+
],
395+
"iterator": {
396+
"$func": {
397+
"$exec": "router1",
398+
"$method": "get",
399+
"$args": {
400+
"path": { "$data": "/path" }
401+
}
402+
},
403+
"$args": ["path"]
404+
}
405+
}
406+
}
407+
```
408+
409+
If the function was previously defined it can be passed either using `"$ref"` with an absolute or relative JSON-pointer or `{ "$func": "myfunc" }. The latter always evaluates as the reference to the existing function rather than the function that always returns string "myfunc".
410+

instructions/$call.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "$call",
3+
"description": "function call",
4+
"keywords": ["$call", "$args"],
5+
"required": ["$call"],
6+
"evaluate": {
7+
"validatorKeyword": "call$func",
8+
"title": "function call",
9+
"description": "calls function"
10+
},
11+
"schema": {
12+
"$call": {
13+
"anyOf": [
14+
{
15+
"type": "string",
16+
"anyOf": [
17+
{ "pattern": "^[A-Za-z_$][A-Za-z_$0-9]+$" },
18+
{ "format": "uuid" }
19+
]
20+
},
21+
{
22+
"description": "custom keyword to validate that value is a function",
23+
"function": true
24+
}
25+
]
26+
}
27+
}
28+
}

instructions/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ module.exports = [
66
require('./$data.json'),
77
require('./$if.json'),
88
require('./$delay.json'),
9-
require('./$func.json')
9+
require('./$func.json'),
10+
require('./$call.json')
1011
];

schema/evaluate.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,66 @@
340340
]
341341
}
342342
},
343+
{
344+
"if": {
345+
"required": [
346+
"$call"
347+
]
348+
},
349+
"then": {
350+
"title": "instruction $call",
351+
"allOf": [
352+
{
353+
"title": "evaluate properties",
354+
"description": "evaluates all or some keywords using this (full) schema, properties-scripts are replaced with returned synchronous or asynchronous value",
355+
"additionalProperties": {
356+
"$ref": "#"
357+
}
358+
},
359+
{
360+
"title": "object to [async] value",
361+
"description": "Merge object properties into a single [asynchronous] object value",
362+
"objectToAsync": true
363+
},
364+
{
365+
"title": "execute instruction",
366+
"description": "executes supported script instructions",
367+
"validateAsync": {
368+
"allOf": [
369+
{
370+
"description": "valdate evaluated instruction keywords",
371+
"properties": {
372+
"$call": {
373+
"anyOf": [
374+
{
375+
"type": "string",
376+
"anyOf": [
377+
{
378+
"pattern": "^[A-Za-z_$][A-Za-z_$0-9]+$"
379+
},
380+
{
381+
"format": "uuid"
382+
}
383+
]
384+
},
385+
{
386+
"description": "custom keyword to validate that value is a function",
387+
"function": true
388+
}
389+
]
390+
}
391+
}
392+
},
393+
{
394+
"description": "execute instruction using custom keyword",
395+
"call$func": true
396+
}
397+
]
398+
}
399+
}
400+
]
401+
}
402+
},
343403
{
344404
"then": {
345405
"title": "parallel execution",

schema/schema.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
{
2323
"$ref": "#$func"
2424
},
25+
{
26+
"$ref": "#$call"
27+
},
2528
{
2629
"$ref": "#parallel"
2730
},
@@ -145,6 +148,24 @@
145148
"$func"
146149
]
147150
},
151+
"_$call": {
152+
"id": "#$call",
153+
"title": "$call",
154+
"description": "function call",
155+
"type": "object",
156+
"properties": {
157+
"$call": {
158+
"$ref": "#"
159+
},
160+
"$args": {
161+
"$ref": "#"
162+
}
163+
},
164+
"additionalProperties": false,
165+
"required": [
166+
"$call"
167+
]
168+
},
148169
"parallel": {
149170
"id": "#parallel",
150171
"description": "scripts in the object are executed in parallel, property names should not start with $",

schema/schema_strict.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
{
2323
"$ref": "#$func"
2424
},
25+
{
26+
"$ref": "#$call"
27+
},
2528
{
2629
"$ref": "#parallel"
2730
},
@@ -291,6 +294,24 @@
291294
"$func"
292295
]
293296
},
297+
"_$call": {
298+
"id": "#$call",
299+
"title": "$call",
300+
"description": "function call",
301+
"type": "object",
302+
"properties": {
303+
"$call": {
304+
"$ref": "#"
305+
},
306+
"$args": {
307+
"$ref": "#"
308+
}
309+
},
310+
"additionalProperties": false,
311+
"required": [
312+
"$call"
313+
]
314+
},
294315
"parallel": {
295316
"id": "#parallel",
296317
"description": "scripts in the object are executed in parallel, property names should not start with $",

spec/scripts/$call.json

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
[
2+
{
3+
"description": "$call - function call",
4+
"schemas": [
5+
{ "$ref": "http://www.json-script.com/schema/schema.json#" },
6+
{ "$ref": "http://www.json-script.com/schema/schema.json#$call" },
7+
{ "$ref": "http://www.json-script.com/schema/schema_strict.json#" },
8+
{ "$ref": "http://www.json-script.com/schema/schema_strict.json#$call" }
9+
],
10+
"tests": [
11+
{
12+
"description": "call named function, no arguments",
13+
"data": {
14+
"$call": "myfunc"
15+
},
16+
"valid": true
17+
},
18+
{
19+
"description": "call named function, args is array",
20+
"data": {
21+
"$call": "myfunc",
22+
"$args": [ 1, 2 ]
23+
},
24+
"valid": true
25+
},
26+
{
27+
"description": "call named function, args is object",
28+
"data": {
29+
"$call": "myfunc",
30+
"$args": { "x": 1, "y": 2 }
31+
},
32+
"valid": true
33+
},
34+
{
35+
"description": "call named function, args is scalar",
36+
"data": {
37+
"$call": "myfunc",
38+
"$args": "foo"
39+
},
40+
"valid": true
41+
},
42+
{
43+
"description": "call inline anonymous function",
44+
"data": {
45+
"$call": {
46+
"$func": {
47+
"$exec": "calc",
48+
"$method": "add",
49+
"$args": [
50+
{ "$data": "/x" },
51+
{ "$data": "/y" }
52+
]
53+
},
54+
"$args": [ "x", "y" ]
55+
},
56+
"$args": { "x": 1, "y": 2 }
57+
},
58+
"valid": true
59+
},
60+
{
61+
"description": "call function by reference",
62+
"data": {
63+
"$call": { "$ref": "/defs/myfunc" },
64+
"$args": { "x": 1, "y": 2 }
65+
},
66+
"valid": true
67+
}
68+
]
69+
},
70+
{
71+
"description": "$call syntax errors",
72+
"schemas": [
73+
{ "$ref": "http://www.json-script.com/schema/schema.json#" },
74+
{ "$ref": "http://www.json-script.com/schema/schema.json#$call" },
75+
{ "$ref": "http://www.json-script.com/schema/schema_strict.json#" },
76+
{ "$ref": "http://www.json-script.com/schema/schema_strict.json#$call" }
77+
],
78+
"tests": [
79+
{
80+
"description": "$call is scalar but not a string (passes validation, fails at eval time)",
81+
"data": { "$call": 1 },
82+
"valid": true
83+
},
84+
{
85+
"description": "$call is invalid string (passes validation, fails at eval time)",
86+
"data": { "$call": "foo%bar" },
87+
"valid": true
88+
},
89+
{
90+
"description": "additional properties",
91+
"data": {
92+
"$call": "myfunc",
93+
"extras": {}
94+
},
95+
"valid": false
96+
}
97+
]
98+
}
99+
]

0 commit comments

Comments
 (0)