@toa.io/userland 1.0.0 → 1.1.0-dev.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/example/components/echo/manifest.toa.yaml +5 -0
- package/example/components/echo/operations/get.js +7 -0
- package/example/components/echo/operations/resolve.js +7 -0
- package/example/components/echo/samples/get.yaml +12 -0
- package/example/components/web/manifest.toa.yaml +8 -0
- package/example/components/web/operations/get.js +10 -0
- package/example/components/web/samples/get.yaml +6 -0
- package/package.json +9 -9
- package/samples/notes.md +2 -2
- package/samples/readme.md +37 -5
- package/samples/src/.replay/.suite/translate/.operation/.prepare/cast.js +20 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/cast.test.js +95 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/expand.js +14 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/index.js +7 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/shortcuts/.aspect.js +26 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/shortcuts/configuration.js +5 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/shortcuts/http.js +5 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/shortcuts/index.js +9 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/shortcuts/state.js +5 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/async.js +3 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/cast.js +26 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/index.js +5 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/map.js +3 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/set.js +3 -0
- package/samples/src/.replay/.suite/translate/.operation/.prepare/types/sync.js +3 -0
- package/samples/src/.replay/.suite/translate/.operation/prepare.js +4 -4
- package/samples/types/operation.d.ts +2 -2
- package/samples/src/.replay/.suite/translate/.operation/specials/configuration.js +0 -26
- package/samples/src/.replay/.suite/translate/.operation/specials/index.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/userland",
|
|
3
|
-
"version": "1.0.0",
|
|
3
|
+
"version": "1.1.0-dev.0",
|
|
4
4
|
"description": "Toa development kit",
|
|
5
5
|
"homepage": "https://toa.io",
|
|
6
6
|
"author": {
|
|
@@ -24,14 +24,14 @@
|
|
|
24
24
|
"main": "src/index.js",
|
|
25
25
|
"types": "types/index.d.ts",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@toa.io/boot": "1.0.0",
|
|
28
|
-
"@toa.io/core": "1.0.0",
|
|
29
|
-
"@toa.io/filesystem": "1.0.0",
|
|
30
|
-
"@toa.io/generic": "0.
|
|
31
|
-
"@toa.io/norm": "1.0.0",
|
|
32
|
-
"@toa.io/schemas": "0.8.
|
|
33
|
-
"@toa.io/yaml": "0.7.
|
|
27
|
+
"@toa.io/boot": "1.0.1-dev.0",
|
|
28
|
+
"@toa.io/core": "1.0.1-dev.0",
|
|
29
|
+
"@toa.io/filesystem": "1.0.1-dev.0",
|
|
30
|
+
"@toa.io/generic": "0.10.0-dev.0",
|
|
31
|
+
"@toa.io/norm": "1.0.1-dev.0",
|
|
32
|
+
"@toa.io/schemas": "0.8.3-dev.0",
|
|
33
|
+
"@toa.io/yaml": "0.7.5-dev.0",
|
|
34
34
|
"tap": "16.3.4"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "5e7998b39ea6111a2cf7d38fad8aeae71d742621"
|
|
37
37
|
}
|
package/samples/notes.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
## Extending Replay
|
|
4
4
|
|
|
5
5
|
1. Update [`example`](../example). `toa replay` should either fail or throw exception.
|
|
6
|
-
2. Add [feature](
|
|
6
|
+
2. Add [feature](/features). It should fail.
|
|
7
7
|
3. If the new decorator is being developed:
|
|
8
8
|
1. Add the [decorator](/extensions/sampling/docs/replay.md).
|
|
9
9
|
2. Add [boot extension](/runtime/boot/src/extensions).
|
|
10
10
|
4. Update `suite` [translation](./src/.suite/.component/translate.js).
|
|
11
|
-
5. Ensure feature is passing.
|
|
11
|
+
5. Ensure the feature is passing.
|
|
12
12
|
6. Ensure example is passing with `toa replay`.
|
package/samples/readme.md
CHANGED
|
@@ -26,14 +26,14 @@ to be verified. See its [schema](./src/.replay/.suite/translate/schemas/operatio
|
|
|
26
26
|
> as [multi-document YAML files](https://yaml.org/spec/1.2.2/#22-structures).
|
|
27
27
|
|
|
28
28
|
Operation samples must be located under the `samples` directory in the component or context root.
|
|
29
|
-
Sample file names must follow the convention: `namespace.
|
|
29
|
+
Sample file names must follow the convention: `namespace.component.operation.yaml`, that is, be an
|
|
30
30
|
endpoint of the operation samples to be applied to. For component-level sample files `namespace`
|
|
31
31
|
and `name` must match corresponding component, therefore are optional.
|
|
32
32
|
|
|
33
33
|
## Message Samples
|
|
34
34
|
|
|
35
35
|
Message Sample is an object containing receiver's input (`payload`) to be substituted and
|
|
36
|
-
outcomes (`input` and `query`) to be verified. Message sample may contain corresponding operation
|
|
36
|
+
outcomes (`input` and `query`) to be verified. Message sample may contain the corresponding operation
|
|
37
37
|
sample. See its [schema](./src/.replay/.suite/translate/schemas/message.cos.yaml).
|
|
38
38
|
|
|
39
39
|
> Message samples are always [autonomous](#autonomy).
|
|
@@ -42,15 +42,47 @@ sample. See its [schema](./src/.replay/.suite/translate/schemas/message.cos.yaml
|
|
|
42
42
|
|
|
43
43
|
Operation samples must be located under the `samples/messages` directory in the component or context
|
|
44
44
|
root. Samples file names must follow the convention `namesace.component.event.yaml`, that is, be a
|
|
45
|
-
label
|
|
46
|
-
|
|
45
|
+
label of the event receiver is consuming.
|
|
46
|
+
|
|
47
|
+
#### Aspect Shortcuts
|
|
48
|
+
|
|
49
|
+
- `configuration` for [Configuration](/extensions/configuration)
|
|
50
|
+
- `state` for [State](/extensions/state)
|
|
51
|
+
- `http` for HTTP Aspect from [Origins](/extensions/origins)
|
|
52
|
+
|
|
53
|
+
#### Aspect Result Type Hints
|
|
54
|
+
|
|
55
|
+
When using aspect calls, there might be situations where the returned values cannot be adequately described using YAML.
|
|
56
|
+
To address this issue, type hints can be used.
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
state:
|
|
60
|
+
values:Map:
|
|
61
|
+
foo: 1
|
|
62
|
+
bar: 2
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
In the above code snippet, the `state` Aspect returns a `values` field of type Map.
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
state:
|
|
69
|
+
values:Set: [foo, bar]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`sync` and `async` hints define functions:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
state:
|
|
76
|
+
get:sync: foo
|
|
77
|
+
request:async: bar
|
|
78
|
+
```
|
|
47
79
|
|
|
48
80
|
## Autonomy
|
|
49
81
|
|
|
50
82
|
Component level samples are *autonomous*, namely, does not assume actual remote calls as
|
|
51
83
|
replaying of component-level samples will boot only that component. Remote call attempt not declared
|
|
52
84
|
within sample will cause an exception.
|
|
53
|
-
See [examples](../example/components/math
|
|
85
|
+
See [examples](../example/components/math.calculations/samples).
|
|
54
86
|
|
|
55
87
|
For context level samples (integration samples), remote calls with non-declared outputs will be
|
|
56
88
|
actually performed. Replaying these samples will boot the composition with all components of the
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { plain } = require('@toa.io/generic')
|
|
4
|
+
const types = require('./types')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {toa.sampling.request.Extensions} extensions
|
|
8
|
+
*/
|
|
9
|
+
function cast (extensions) {
|
|
10
|
+
for (const calls of Object.values(extensions)) calls.map(resolve)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {toa.sampling.request.extensions.Call} call
|
|
15
|
+
*/
|
|
16
|
+
function resolve (call) {
|
|
17
|
+
if (plain(call.result)) call.result = types.cast(call.result)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
exports.cast = cast
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { cast } = require('./')
|
|
4
|
+
|
|
5
|
+
it('should be', async () => {
|
|
6
|
+
expect(cast).toBeInstanceOf(Function)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it('should cast to a Map', async () => {
|
|
10
|
+
/** @type {toa.sampling.request.Extensions} */
|
|
11
|
+
const extensions = {
|
|
12
|
+
state: [{
|
|
13
|
+
result: {
|
|
14
|
+
'value:Map': { foo: 'bar' }
|
|
15
|
+
}
|
|
16
|
+
}]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
cast(extensions)
|
|
20
|
+
|
|
21
|
+
const value = /** @type {Map} */ extensions.state[0].result.value
|
|
22
|
+
|
|
23
|
+
expect(value).toBeDefined()
|
|
24
|
+
expect(value).toBeInstanceOf(Map)
|
|
25
|
+
expect(Array.from(value.keys())).toStrictEqual(['foo'])
|
|
26
|
+
expect(value.get('foo')).toStrictEqual('bar')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('should not throw if result is undefined', async () => {
|
|
30
|
+
const extensions = { state: [{}] }
|
|
31
|
+
|
|
32
|
+
expect(() => cast(extensions)).not.toThrow()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should cast to a Set', async () => {
|
|
36
|
+
const names = ['Mary', 'Bob', 'Elizabeth']
|
|
37
|
+
|
|
38
|
+
/** @type {toa.sampling.request.Extensions} */
|
|
39
|
+
const extensions = {
|
|
40
|
+
state: [{
|
|
41
|
+
result: {
|
|
42
|
+
'names:Set': names
|
|
43
|
+
}
|
|
44
|
+
}]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
cast(extensions)
|
|
48
|
+
|
|
49
|
+
const set = /** @type {Map} */ extensions.state[0].result.names
|
|
50
|
+
|
|
51
|
+
expect(set).toBeDefined()
|
|
52
|
+
expect(set).toBeInstanceOf(Set)
|
|
53
|
+
expect(Array.from(set)).toStrictEqual(names)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should cast to a Function', async () => {
|
|
57
|
+
const extensions = {
|
|
58
|
+
foo: [{
|
|
59
|
+
result: {
|
|
60
|
+
'resolve:sync': 'hello'
|
|
61
|
+
}
|
|
62
|
+
}]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
cast(extensions)
|
|
66
|
+
|
|
67
|
+
const resolve = extensions.foo[0].result.resolve
|
|
68
|
+
|
|
69
|
+
expect(resolve).toBeDefined()
|
|
70
|
+
expect(resolve).toBeInstanceOf(Function)
|
|
71
|
+
expect(resolve()).toStrictEqual('hello')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should cast to async Function', async () => {
|
|
75
|
+
const extensions = {
|
|
76
|
+
foo: [{
|
|
77
|
+
result: {
|
|
78
|
+
'resolve:async': 'hello'
|
|
79
|
+
}
|
|
80
|
+
}]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
cast(extensions)
|
|
84
|
+
|
|
85
|
+
const resolve = extensions.foo[0].result.resolve
|
|
86
|
+
|
|
87
|
+
expect(resolve).toBeDefined()
|
|
88
|
+
expect(resolve).toBeInstanceOf(Function)
|
|
89
|
+
|
|
90
|
+
const promise = resolve()
|
|
91
|
+
|
|
92
|
+
expect(promise).toBeInstanceOf(Promise)
|
|
93
|
+
await expect(promise).resolves.toStrictEqual('hello')
|
|
94
|
+
})
|
|
95
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const shortcuts = require('./shortcuts')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {toa.samples.Operation & Object} declaration
|
|
7
|
+
*/
|
|
8
|
+
function expand (declaration) {
|
|
9
|
+
for (const [shortcut, expand] of Object.entries(shortcuts)) {
|
|
10
|
+
if (shortcut in declaration) expand(declaration)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.expand = expand
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const aspect = (name) =>
|
|
4
|
+
/**
|
|
5
|
+
* @param {toa.samples.Operation & Record<string, any>} declaration
|
|
6
|
+
*/
|
|
7
|
+
(declaration) => {
|
|
8
|
+
const value = declaration[name]
|
|
9
|
+
|
|
10
|
+
delete declaration[name]
|
|
11
|
+
|
|
12
|
+
if (declaration.extensions === undefined) declaration.extensions = {}
|
|
13
|
+
|
|
14
|
+
if (name in declaration.extensions) throw new Error(`${name} aspect sample is ambiguous`)
|
|
15
|
+
|
|
16
|
+
/** @type {toa.sampling.request.extensions.Call} */
|
|
17
|
+
const call = {
|
|
18
|
+
result: value,
|
|
19
|
+
permanent: true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declaration.extensions[name] = [call]
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.aspect = aspect
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { map } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
const types = {
|
|
6
|
+
Map: require('./map'),
|
|
7
|
+
Set: require('./set'),
|
|
8
|
+
sync: require('./sync'),
|
|
9
|
+
async: require('./async')
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function cast (value) {
|
|
13
|
+
return map(value, replace)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function replace (key, value) {
|
|
17
|
+
const [name, type] = key.split(':')
|
|
18
|
+
|
|
19
|
+
if (!(type in types)) return
|
|
20
|
+
|
|
21
|
+
const typed = types[type](value)
|
|
22
|
+
|
|
23
|
+
return [name, typed]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.cast = cast
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { cast, expand } = require('./.prepare')
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {toa.samples.Operation & Object} declaration
|
|
7
7
|
*/
|
|
8
8
|
const prepare = (declaration) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
expand(declaration)
|
|
10
|
+
|
|
11
|
+
if ('extensions' in declaration) cast(declaration.extensions)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
exports.prepare = prepare
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _core from '@toa.io/core/types'
|
|
2
|
-
import * as _sampling from '@toa.io/extensions.sampling'
|
|
2
|
+
import * as _sampling from '@toa.io/extensions.sampling/types/request'
|
|
3
3
|
|
|
4
4
|
declare namespace toa.samples {
|
|
5
5
|
|
|
@@ -27,7 +27,7 @@ declare namespace toa.samples {
|
|
|
27
27
|
remote?: operations.Calls
|
|
28
28
|
local?: operations.Calls
|
|
29
29
|
events?: operations.Events
|
|
30
|
-
extensions?: _sampling.
|
|
30
|
+
extensions?: _sampling.Extensions
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {toa.samples.Operation & Object} declaration
|
|
5
|
-
*/
|
|
6
|
-
const configuration = (declaration) => {
|
|
7
|
-
const configuration = declaration.configuration
|
|
8
|
-
|
|
9
|
-
delete declaration.configuration
|
|
10
|
-
|
|
11
|
-
if (declaration.extensions === undefined) declaration.extensions = {}
|
|
12
|
-
|
|
13
|
-
if ('configuration' in declaration.extensions) {
|
|
14
|
-
throw new Error('Configuration extension sample is ambiguous')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** @type {toa.sampling.request.extensions.Call} */
|
|
18
|
-
const call = {
|
|
19
|
-
result: configuration,
|
|
20
|
-
permanent: true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
declaration.extensions.configuration = [call]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
exports.configuration = configuration
|