@toa.io/norm 0.20.0-dev.9 → 0.20.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/package.json +8 -7
- package/src/.component/.expand/extensions.js +4 -1
- package/src/.component/.expand/operations.js +4 -1
- package/src/.component/.normalize/index.js +0 -2
- package/src/.component/.normalize/operations.js +4 -0
- package/src/.component/{.normalize/extensions.js → extensions.js} +1 -4
- package/src/.component/index.js +2 -0
- package/src/.component/normalize.js +2 -5
- package/src/.component/schema.yaml +15 -8
- package/src/.context/.dependencies/connectors.js +7 -7
- package/src/.context/.dependencies/extensions.js +44 -12
- package/src/.context/.dependencies/resolve.js +2 -8
- package/src/.context/dependencies.js +4 -7
- package/src/component.js +4 -5
- package/src/context.js +1 -4
- package/src/shortcuts.js +3 -1
- package/test/component/normalize.fixtures.js +6 -2
- package/test/component/normalize.test.js +0 -13
- package/test/component/validate.fixtures.js +10 -0
- package/test/component/validate.test.js +15 -0
- package/types/component.d.ts +52 -71
- package/types/context/declaration.d.ts +17 -0
- package/types/context.d.ts +31 -66
- package/types/index.d.ts +3 -4
- package/features/component.operations.feature +0 -54
- package/features/steps/.test/manifest.test.js +0 -111
- package/features/steps/.types/context.d.ts +0 -9
- package/features/steps/hooks.js +0 -18
- package/features/steps/manifest.js +0 -66
- package/features/steps/parameters.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/norm",
|
|
3
|
-
"version": "0.20.0
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Toa declarations normalization and validation",
|
|
5
5
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"url": "https://github.com/toa-io/toa/issues"
|
|
13
13
|
},
|
|
14
14
|
"main": "src/index.js",
|
|
15
|
+
"types": "types/index.d.ts",
|
|
15
16
|
"publishConfig": {
|
|
16
17
|
"access": "public"
|
|
17
18
|
},
|
|
@@ -19,11 +20,11 @@
|
|
|
19
20
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@toa.io/core": "0.20.0
|
|
23
|
-
"@toa.io/generic": "0.20.0
|
|
24
|
-
"@toa.io/schema": "0.20.0
|
|
25
|
-
"@toa.io/schemas": "0.20.0
|
|
26
|
-
"@toa.io/yaml": "0.20.0
|
|
23
|
+
"@toa.io/core": "0.20.0",
|
|
24
|
+
"@toa.io/generic": "0.20.0",
|
|
25
|
+
"@toa.io/schema": "0.20.0",
|
|
26
|
+
"@toa.io/schemas": "0.20.0",
|
|
27
|
+
"@toa.io/yaml": "0.20.0"
|
|
27
28
|
},
|
|
28
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "28fc4b45c224c3683acaaf0e4abd1eb04e07b408"
|
|
29
30
|
}
|
|
@@ -9,8 +9,11 @@ function extensions (manifest) {
|
|
|
9
9
|
|
|
10
10
|
const SHORTCUTS = {
|
|
11
11
|
exposition: '@toa.io/extensions.exposition',
|
|
12
|
+
realtime: '@toa.io/extensions.realtime',
|
|
12
13
|
origins: '@toa.io/extensions.origins',
|
|
13
|
-
configuration: '@toa.io/extensions.configuration'
|
|
14
|
+
configuration: '@toa.io/extensions.configuration',
|
|
15
|
+
state: '@toa.io/extensions.state',
|
|
16
|
+
stash: '@toa.io/extensions.stash'
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
exports.extensions = extensions
|
|
@@ -10,7 +10,10 @@ function operations (manifest) {
|
|
|
10
10
|
if (operation.input !== undefined) operation.input = expand(operation.input)
|
|
11
11
|
if (operation.output !== undefined) operation.output = expand(operation.output)
|
|
12
12
|
if (operation.bridge !== undefined) operation.bridge = resolve(operation.bridge)
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
if (operation.bindings !== undefined && operation.bindings !== null) {
|
|
15
|
+
operation.bindings = operation.bindings.map(resolve)
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { events } = require('./events')
|
|
4
|
-
const { extensions } = require('./extensions')
|
|
5
4
|
const { operations } = require('./operations')
|
|
6
5
|
const { receivers } = require('./receivers')
|
|
7
6
|
|
|
8
7
|
exports.events = events
|
|
9
|
-
exports.extensions = extensions
|
|
10
8
|
exports.operations = operations
|
|
11
9
|
exports.receivers = receivers
|
|
@@ -4,7 +4,11 @@ const operations = (component) => {
|
|
|
4
4
|
if (component.operations === undefined) return
|
|
5
5
|
|
|
6
6
|
for (const [endpoint, operation] of Object.entries(component.operations)) {
|
|
7
|
+
if (operation.type === 'computation' || operation.type === 'effect')
|
|
8
|
+
operation.query = false
|
|
9
|
+
|
|
7
10
|
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
11
|
+
if (operation.bindings === null) operation.bindings = []
|
|
8
12
|
if (operation.virtual === true) delete component.operations[endpoint]
|
|
9
13
|
}
|
|
10
14
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { directory: { find } } = require('@toa.io/filesystem')
|
|
4
|
-
const { resolve } = require('
|
|
4
|
+
const { resolve } = require('../shortcuts')
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* @param {toa.norm.Component} manifest
|
|
8
|
-
*/
|
|
9
6
|
const extensions = (manifest) => {
|
|
10
7
|
if (manifest.extensions === undefined) return
|
|
11
8
|
|
package/src/.component/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const { expand } = require('./expand')
|
|
|
8
8
|
const { merge } = require('./merge')
|
|
9
9
|
const { normalize } = require('./normalize')
|
|
10
10
|
const { validate } = require('./validate')
|
|
11
|
+
const { extensions } = require('./extensions')
|
|
11
12
|
|
|
12
13
|
exports.collapse = collapse
|
|
13
14
|
exports.defaults = defaults
|
|
@@ -17,3 +18,4 @@ exports.expand = expand
|
|
|
17
18
|
exports.merge = merge
|
|
18
19
|
exports.normalize = normalize
|
|
19
20
|
exports.validate = validate
|
|
21
|
+
exports.extensions = extensions
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const { events, operations, extensions, receivers } = require('./.normalize')
|
|
3
|
+
const { events, operations, receivers } = require('./.normalize')
|
|
5
4
|
|
|
6
|
-
const normalize = (component
|
|
7
|
-
convolve(component, environment)
|
|
5
|
+
const normalize = (component) => {
|
|
8
6
|
operations(component)
|
|
9
7
|
events(component)
|
|
10
8
|
receivers(component)
|
|
11
|
-
extensions(component)
|
|
12
9
|
}
|
|
13
10
|
|
|
14
11
|
exports.normalize = normalize
|
|
@@ -20,7 +20,7 @@ properties:
|
|
|
20
20
|
operations:
|
|
21
21
|
type: object
|
|
22
22
|
propertyNames:
|
|
23
|
-
$ref: 'definitions#/definitions/
|
|
23
|
+
$ref: 'definitions#/definitions/name'
|
|
24
24
|
patternProperties:
|
|
25
25
|
'.*':
|
|
26
26
|
type: object
|
|
@@ -68,7 +68,7 @@ properties:
|
|
|
68
68
|
type: object
|
|
69
69
|
propertyNames:
|
|
70
70
|
oneOf:
|
|
71
|
-
- $ref: 'definitions#/definitions/
|
|
71
|
+
- $ref: 'definitions#/definitions/name'
|
|
72
72
|
- enum: [_version]
|
|
73
73
|
initialized:
|
|
74
74
|
type: boolean
|
|
@@ -79,7 +79,6 @@ properties:
|
|
|
79
79
|
bindings:
|
|
80
80
|
type: array
|
|
81
81
|
uniqueItems: true
|
|
82
|
-
minItems: 1
|
|
83
82
|
items:
|
|
84
83
|
$ref: '#/definitions/binding'
|
|
85
84
|
|
|
@@ -89,7 +88,7 @@ properties:
|
|
|
89
88
|
operations:
|
|
90
89
|
type: object
|
|
91
90
|
propertyNames:
|
|
92
|
-
$ref: 'definitions#/definitions/
|
|
91
|
+
$ref: 'definitions#/definitions/name'
|
|
93
92
|
patternProperties:
|
|
94
93
|
'.*':
|
|
95
94
|
type: object
|
|
@@ -101,7 +100,7 @@ properties:
|
|
|
101
100
|
concurrency:
|
|
102
101
|
enum: [none, retry]
|
|
103
102
|
forward:
|
|
104
|
-
$ref: 'definitions#/definitions/
|
|
103
|
+
$ref: 'definitions#/definitions/name'
|
|
105
104
|
bridge:
|
|
106
105
|
type: string
|
|
107
106
|
bindings:
|
|
@@ -167,6 +166,9 @@ properties:
|
|
|
167
166
|
scope:
|
|
168
167
|
const: none
|
|
169
168
|
default: none
|
|
169
|
+
query:
|
|
170
|
+
const: false
|
|
171
|
+
default: false
|
|
170
172
|
- if: # effect
|
|
171
173
|
properties:
|
|
172
174
|
type:
|
|
@@ -176,13 +178,16 @@ properties:
|
|
|
176
178
|
scope:
|
|
177
179
|
const: none
|
|
178
180
|
default: none
|
|
181
|
+
query:
|
|
182
|
+
const: false
|
|
183
|
+
default: false
|
|
179
184
|
additionalProperties: false
|
|
180
185
|
additionalProperties: false
|
|
181
186
|
|
|
182
187
|
events:
|
|
183
188
|
type: object
|
|
184
189
|
propertyNames:
|
|
185
|
-
$ref: 'definitions#/definitions/
|
|
190
|
+
$ref: 'definitions#/definitions/name'
|
|
186
191
|
patternProperties:
|
|
187
192
|
'.*':
|
|
188
193
|
type: object
|
|
@@ -209,13 +214,15 @@ properties:
|
|
|
209
214
|
type: object
|
|
210
215
|
properties:
|
|
211
216
|
operation:
|
|
212
|
-
$ref: 'definitions#/definitions/
|
|
217
|
+
$ref: 'definitions#/definitions/name'
|
|
213
218
|
bridge:
|
|
214
219
|
type: string
|
|
215
220
|
binding:
|
|
216
221
|
type: string
|
|
217
222
|
source:
|
|
218
|
-
$ref: 'definitions#/definitions/
|
|
223
|
+
$ref: 'definitions#/definitions/name'
|
|
224
|
+
not:
|
|
225
|
+
const: context
|
|
219
226
|
path:
|
|
220
227
|
type: string
|
|
221
228
|
conditioned:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* @param {toa.norm.Context} context
|
|
5
|
-
* @returns {toa.norm.context.dependencies.References}
|
|
6
|
-
*/
|
|
7
|
-
const connectors = (context) => {
|
|
8
|
-
/** @type {toa.norm.context.dependencies.References} */
|
|
3
|
+
const connectors = (context, extracted) => {
|
|
9
4
|
const connectors = {}
|
|
10
5
|
|
|
11
|
-
|
|
6
|
+
const components = (context.components === undefined
|
|
7
|
+
? extracted
|
|
8
|
+
: context.components.concat(extracted)
|
|
9
|
+
) ?? []
|
|
10
|
+
|
|
11
|
+
for (const component of components) {
|
|
12
12
|
if (connectors[component.entity.storage] === undefined) {
|
|
13
13
|
connectors[component.entity.storage] = []
|
|
14
14
|
}
|
|
@@ -1,24 +1,56 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
* @param {toa.norm.Context} context
|
|
5
|
-
* @returns {toa.norm.context.dependencies.References}
|
|
6
|
-
*/
|
|
7
|
-
const extensions = (context) => {
|
|
8
|
-
/** @type {toa.norm.context.dependencies.References} */
|
|
3
|
+
const extensions = async (context) => {
|
|
9
4
|
const extensions = {}
|
|
5
|
+
const components = context.components?.slice() ?? []
|
|
6
|
+
const extracted = await extractExtensionComponents(components, extensions)
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
if (component.extensions !== undefined) {
|
|
13
|
-
for (const reference of Object.keys(component.extensions)) {
|
|
14
|
-
if (extensions[reference] === undefined) extensions[reference] = []
|
|
8
|
+
components.push(...extracted)
|
|
15
9
|
|
|
16
|
-
|
|
10
|
+
for (const component of components) {
|
|
11
|
+
if (component.extensions === undefined) continue
|
|
12
|
+
|
|
13
|
+
for (const reference of Object.keys(component.extensions)) {
|
|
14
|
+
if (extensions[reference] === undefined) extensions[reference] = []
|
|
15
|
+
|
|
16
|
+
extensions[reference].push(component)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return { extensions, components: extracted }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function extractExtensionComponents (components, extensions) {
|
|
24
|
+
const { component: load } = require('../../component')
|
|
25
|
+
|
|
26
|
+
const extracted = []
|
|
27
|
+
|
|
28
|
+
for (const component of components) {
|
|
29
|
+
if (component.extensions === undefined) continue
|
|
30
|
+
|
|
31
|
+
for (const reference of Object.keys(component.extensions)) {
|
|
32
|
+
if (reference in extensions) continue
|
|
33
|
+
|
|
34
|
+
extensions[reference] = []
|
|
35
|
+
|
|
36
|
+
const mod = require(reference)
|
|
37
|
+
|
|
38
|
+
if (mod.components === undefined) continue
|
|
39
|
+
|
|
40
|
+
for (const path of mod.components().paths) {
|
|
41
|
+
const component = await load(path)
|
|
42
|
+
|
|
43
|
+
extracted.push(component)
|
|
17
44
|
}
|
|
18
45
|
}
|
|
19
46
|
}
|
|
20
47
|
|
|
21
|
-
|
|
48
|
+
if (extracted.length === 0)
|
|
49
|
+
return extracted
|
|
50
|
+
|
|
51
|
+
const deeper = await extractExtensionComponents(extracted, extensions)
|
|
52
|
+
|
|
53
|
+
return extracted.concat(deeper)
|
|
22
54
|
}
|
|
23
55
|
|
|
24
56
|
exports.extensions = extensions
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { join } = require('node:path')
|
|
4
3
|
const { load } = require('./load')
|
|
5
4
|
|
|
6
|
-
/**
|
|
7
|
-
* @param {toa.norm.context.dependencies.References} references
|
|
8
|
-
* @param {Object} annotations
|
|
9
|
-
* @returns {toa.norm.context.Dependencies}
|
|
10
|
-
*/
|
|
11
5
|
const resolve = (references, annotations) => {
|
|
12
|
-
/** @type {toa.norm.context.Dependencies} */
|
|
13
6
|
const dependencies = {}
|
|
14
7
|
|
|
15
8
|
for (const [dependency, components] of Object.entries(references)) {
|
|
@@ -18,7 +11,8 @@ const resolve = (references, annotations) => {
|
|
|
18
11
|
|
|
19
12
|
const instances = components.map((component) => ({
|
|
20
13
|
locator: component.locator,
|
|
21
|
-
manifest: component.extensions?.[id]
|
|
14
|
+
manifest: component.extensions?.[id],
|
|
15
|
+
component
|
|
22
16
|
}))
|
|
23
17
|
|
|
24
18
|
dependencies[dependency] = instances
|
|
@@ -2,13 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const { connectors, extensions, resolve } = require('./.dependencies')
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const dependencies = (context) => {
|
|
10
|
-
/** @type {toa.norm.context.dependencies.References} */
|
|
11
|
-
const references = { ...connectors(context), ...extensions(context) }
|
|
5
|
+
const dependencies = async (context) => {
|
|
6
|
+
const { extensions: e, components } = await extensions(context)
|
|
7
|
+
const c = connectors(context, components)
|
|
8
|
+
const references = { ...c, ...e }
|
|
12
9
|
|
|
13
10
|
return resolve(references, context.annotations)
|
|
14
11
|
}
|
package/src/component.js
CHANGED
|
@@ -14,17 +14,16 @@ const {
|
|
|
14
14
|
dereference,
|
|
15
15
|
defaults,
|
|
16
16
|
dependencies,
|
|
17
|
-
normalize
|
|
17
|
+
normalize,
|
|
18
|
+
extensions
|
|
18
19
|
} = require('./.component')
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
* @type {toa.norm.component.Constructor}
|
|
22
|
-
*/
|
|
23
21
|
const component = async (path) => {
|
|
24
22
|
const manifest = await load(path)
|
|
25
23
|
|
|
26
|
-
normalize(manifest
|
|
24
|
+
normalize(manifest)
|
|
27
25
|
validate(manifest)
|
|
26
|
+
extensions(manifest)
|
|
28
27
|
|
|
29
28
|
manifest.locator = new Locator(manifest.name, manifest.namespace)
|
|
30
29
|
|
package/src/context.js
CHANGED
|
@@ -16,9 +16,6 @@ const {
|
|
|
16
16
|
validate
|
|
17
17
|
} = require('./.context')
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* @type {toa.norm.context.Constructor}
|
|
21
|
-
*/
|
|
22
19
|
const context = async (root, environment = process.env.TOA_ENV) => {
|
|
23
20
|
const path = resolve(root, CONTEXT)
|
|
24
21
|
const context = /** @type {toa.norm.Context} */ await load(path)
|
|
@@ -35,7 +32,7 @@ const context = async (root, environment = process.env.TOA_ENV) => {
|
|
|
35
32
|
const paths = await glob(pattern)
|
|
36
33
|
|
|
37
34
|
context.components = await Promise.all(paths.map(component))
|
|
38
|
-
context.dependencies = dependencies(context)
|
|
35
|
+
context.dependencies = await dependencies(context)
|
|
39
36
|
|
|
40
37
|
dereference(context)
|
|
41
38
|
complete(context)
|
package/src/shortcuts.js
CHANGED
|
@@ -46,8 +46,10 @@ const SHORTCUTS = {
|
|
|
46
46
|
sql: '@toa.io/storages.sql',
|
|
47
47
|
queues: '@toa.io/storages.queues',
|
|
48
48
|
exposition: '@toa.io/extensions.exposition',
|
|
49
|
+
realtime: '@toa.io/extensions.realtime',
|
|
49
50
|
configuration: '@toa.io/extensions.configuration',
|
|
50
|
-
origins: '@toa.io/extensions.origins'
|
|
51
|
+
origins: '@toa.io/extensions.origins',
|
|
52
|
+
stash: '@toa.io/extensions.stash'
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
exports.recognize = recognize
|
|
@@ -3,15 +3,19 @@
|
|
|
3
3
|
const { generate } = require('randomstring')
|
|
4
4
|
|
|
5
5
|
const operations = {
|
|
6
|
+
namespace: 'dummies',
|
|
7
|
+
name: 'dummy',
|
|
6
8
|
path: __dirname,
|
|
7
9
|
bindings: ['foo', 'bar'],
|
|
8
10
|
'bindings@local': ['foo'],
|
|
9
11
|
operations: {
|
|
10
|
-
add: {
|
|
12
|
+
add: {
|
|
13
|
+
type: 'assignment'
|
|
14
|
+
}
|
|
11
15
|
},
|
|
12
16
|
extensions: {
|
|
13
17
|
'@toa.io/extensions.exposition': {
|
|
14
|
-
['/' + generate()]:
|
|
18
|
+
['/' + generate()]: {}
|
|
15
19
|
},
|
|
16
20
|
'./dummies/extension': {
|
|
17
21
|
ok: true
|
|
@@ -12,13 +12,6 @@ beforeEach(() => {
|
|
|
12
12
|
manifest = clone(fixtures.operations)
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
describe('environment', () => {
|
|
16
|
-
it('should convolve with environment argument', () => {
|
|
17
|
-
normalize(manifest, 'local')
|
|
18
|
-
expect(manifest.operations.add.bindings).toStrictEqual(['foo'])
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
|
|
22
15
|
describe('operations', () => {
|
|
23
16
|
it('should set default bindings', () => {
|
|
24
17
|
normalize(manifest)
|
|
@@ -36,12 +29,6 @@ describe('extensions', () => {
|
|
|
36
29
|
|
|
37
30
|
expect(manifest.extensions[path]).toStrictEqual(origins)
|
|
38
31
|
})
|
|
39
|
-
|
|
40
|
-
it('should throw if manifest is undefined', () => {
|
|
41
|
-
manifest.extensions['./dummies/extension'].ok = false
|
|
42
|
-
|
|
43
|
-
expect(() => normalize(manifest)).toThrow(/hasn't returned manifest/)
|
|
44
|
-
})
|
|
45
32
|
})
|
|
46
33
|
|
|
47
34
|
describe('receivers', () => {
|
|
@@ -34,6 +34,16 @@ const ok = {
|
|
|
34
34
|
scope: 'changeset',
|
|
35
35
|
bridge: 'whatever',
|
|
36
36
|
bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
|
|
37
|
+
},
|
|
38
|
+
compute: {
|
|
39
|
+
type: 'computation',
|
|
40
|
+
bridge: 'whatever',
|
|
41
|
+
bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
|
|
42
|
+
},
|
|
43
|
+
affect: {
|
|
44
|
+
type: 'effect',
|
|
45
|
+
bridge: 'whatever',
|
|
46
|
+
bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
|
|
37
47
|
}
|
|
38
48
|
},
|
|
39
49
|
events: {
|
|
@@ -172,6 +172,15 @@ describe('operations', () => {
|
|
|
172
172
|
expect(() => validate(manifest)).toThrow(/must NOT be valid/)
|
|
173
173
|
})
|
|
174
174
|
|
|
175
|
+
it.each([
|
|
176
|
+
['computation', 'compute'],
|
|
177
|
+
['effect', 'affect']
|
|
178
|
+
])('should set query: false for %s', async (_, operation) => {
|
|
179
|
+
validate(manifest)
|
|
180
|
+
|
|
181
|
+
expect(manifest.operations[operation].query).toBe(false)
|
|
182
|
+
})
|
|
183
|
+
|
|
175
184
|
describe('scope', () => {
|
|
176
185
|
it('should have scope', () => {
|
|
177
186
|
delete manifest.operations.get.scope
|
|
@@ -240,4 +249,10 @@ describe('receivers', () => {
|
|
|
240
249
|
|
|
241
250
|
expect(() => validate(manifest)).toThrow(/one of the allowed types/)
|
|
242
251
|
})
|
|
252
|
+
|
|
253
|
+
it('should throw if source has a name `context`', async () => {
|
|
254
|
+
manifest.receivers['foo.bar.happened'].source = 'context'
|
|
255
|
+
|
|
256
|
+
expect(() => validate(manifest)).toThrow(/must NOT be valid/)
|
|
257
|
+
})
|
|
243
258
|
})
|
package/types/component.d.ts
CHANGED
|
@@ -1,81 +1,62 @@
|
|
|
1
|
-
import type {Locator} from '@toa.io/core
|
|
1
|
+
import type { Locator, operations } from '@toa.io/core'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
namespace operations {
|
|
7
|
-
|
|
8
|
-
type Type = 'transition' | 'observation' | 'assignment' | 'computation' | 'effect'
|
|
9
|
-
type Scope = 'object' | 'objects' | 'changeset'
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface Map {
|
|
14
|
-
[id: string]: Component
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface Operation {
|
|
18
|
-
type?: operations.Type
|
|
19
|
-
scope?: operations.Scope
|
|
20
|
-
bindings?: string[]
|
|
21
|
-
input?: any
|
|
22
|
-
output?: any
|
|
23
|
-
error?: any
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface Operations {
|
|
27
|
-
[key: string]: Operation
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface Event {
|
|
31
|
-
binding: string
|
|
32
|
-
}
|
|
3
|
+
type Map = {
|
|
4
|
+
[id: string]: Component
|
|
5
|
+
}
|
|
33
6
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
7
|
+
type Operation = {
|
|
8
|
+
type: operations.type
|
|
9
|
+
scope?: operations.scope
|
|
10
|
+
bindings?: string[]
|
|
11
|
+
input?: any
|
|
12
|
+
output?: any
|
|
13
|
+
error?: any
|
|
14
|
+
query?: boolean
|
|
15
|
+
}
|
|
37
16
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
conditioned: boolean
|
|
42
|
-
bridge: string
|
|
43
|
-
binding: string
|
|
44
|
-
path: string
|
|
45
|
-
source?: string
|
|
46
|
-
}
|
|
17
|
+
type Operations = {
|
|
18
|
+
[key: string]: Operation
|
|
19
|
+
}
|
|
47
20
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
initialized?: boolean
|
|
52
|
-
}
|
|
21
|
+
type Event = {
|
|
22
|
+
binding: string
|
|
23
|
+
}
|
|
53
24
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
name: string
|
|
58
|
-
version: string
|
|
59
|
-
entity: Entity
|
|
60
|
-
bindings: string[]
|
|
61
|
-
operations?: Operations
|
|
62
|
-
events?: Events
|
|
63
|
-
receivers: Record<string, Receiver>
|
|
64
|
-
extensions?: Record<string, object>
|
|
65
|
-
properties?: Record<string, object>
|
|
66
|
-
}
|
|
25
|
+
type Events = {
|
|
26
|
+
[key: string]: Event
|
|
27
|
+
}
|
|
67
28
|
|
|
68
|
-
|
|
69
|
-
|
|
29
|
+
type Receiver = {
|
|
30
|
+
operation: string
|
|
31
|
+
adaptive: boolean
|
|
32
|
+
conditioned: boolean
|
|
33
|
+
bridge: string
|
|
34
|
+
binding: string
|
|
35
|
+
path: string
|
|
36
|
+
source?: string
|
|
37
|
+
}
|
|
70
38
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
39
|
+
type Entity = {
|
|
40
|
+
schema: Object
|
|
41
|
+
storage?: string
|
|
42
|
+
initialized?: boolean
|
|
75
43
|
}
|
|
76
44
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
45
|
+
type Declaration = {
|
|
46
|
+
prototype?: string
|
|
47
|
+
namespace: string
|
|
48
|
+
name: string
|
|
49
|
+
version: string
|
|
50
|
+
entity?: Entity
|
|
51
|
+
bindings?: string[]
|
|
52
|
+
operations: Operations
|
|
53
|
+
events?: Events
|
|
54
|
+
receivers?: Record<string, Receiver>
|
|
55
|
+
extensions?: Record<string, object>
|
|
56
|
+
properties?: Record<string, object>
|
|
57
|
+
}
|
|
80
58
|
|
|
81
|
-
export
|
|
59
|
+
export type Manifest = Declaration & {
|
|
60
|
+
locator: Locator
|
|
61
|
+
path: string
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Composition, Registry, Runtime } from '../context'
|
|
2
|
+
|
|
3
|
+
interface Composition {
|
|
4
|
+
name: string,
|
|
5
|
+
components: string[]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Declaration {
|
|
9
|
+
name: string
|
|
10
|
+
description?: string
|
|
11
|
+
version?: string
|
|
12
|
+
runtime?: Runtime | string
|
|
13
|
+
registry?: Registry | string
|
|
14
|
+
packages?: string
|
|
15
|
+
compositions?: Composition[]
|
|
16
|
+
annotations?: Record<string, object>
|
|
17
|
+
}
|
package/types/context.d.ts
CHANGED
|
@@ -1,76 +1,41 @@
|
|
|
1
1
|
import * as _component from './component'
|
|
2
2
|
import { Locator } from '@toa.io/core/types'
|
|
3
|
+
import type { Declaration } from './context/declaration'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
version: string
|
|
10
|
-
registry?: string
|
|
11
|
-
proxy?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface Registry {
|
|
15
|
-
base?: string
|
|
16
|
-
platforms?: string[] | null
|
|
17
|
-
build?: {
|
|
18
|
-
arguments?: string[]
|
|
19
|
-
run?: string
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface Composition {
|
|
24
|
-
name: string,
|
|
25
|
-
components: string[] | _component.Component[]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
namespace dependencies {
|
|
29
|
-
|
|
30
|
-
type Instance = {
|
|
31
|
-
locator: Locator
|
|
32
|
-
manifest?: Object
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type References = {
|
|
36
|
-
[reference: string]: _component.Component[]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
interface Dependencies {
|
|
42
|
-
[reference: string]: dependencies.Instance[]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface Declaration {
|
|
46
|
-
name: string
|
|
47
|
-
description?: string
|
|
48
|
-
version?: string
|
|
49
|
-
runtime?: Runtime | string
|
|
50
|
-
registry?: Registry | string
|
|
51
|
-
packages?: string
|
|
52
|
-
compositions?: Composition[]
|
|
53
|
-
annotations?: Record<string, object>
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type Constructor = (path: string, environment?: string) => Promise<Context>
|
|
57
|
-
}
|
|
5
|
+
interface Runtime {
|
|
6
|
+
version: string
|
|
7
|
+
registry?: string
|
|
8
|
+
proxy?: string
|
|
9
|
+
}
|
|
58
10
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
11
|
+
interface Registry {
|
|
12
|
+
base?: string
|
|
13
|
+
platforms?: string[] | null
|
|
14
|
+
build?: {
|
|
15
|
+
arguments?: string[]
|
|
16
|
+
run?: string
|
|
17
|
+
},
|
|
18
|
+
credentials: string
|
|
19
|
+
}
|
|
66
20
|
|
|
21
|
+
interface Composition {
|
|
22
|
+
name: string,
|
|
23
|
+
components: _component.Component[]
|
|
67
24
|
}
|
|
68
25
|
|
|
69
|
-
export
|
|
70
|
-
|
|
26
|
+
export interface Dependency<T = undefined> {
|
|
27
|
+
locator: Locator
|
|
28
|
+
manifest: T,
|
|
29
|
+
component: _component.Component
|
|
30
|
+
}
|
|
71
31
|
|
|
72
|
-
|
|
73
|
-
|
|
32
|
+
interface Context extends Declaration {
|
|
33
|
+
runtime?: Runtime
|
|
34
|
+
environment?: string
|
|
35
|
+
registry?: Registry
|
|
36
|
+
compositions?: Composition[]
|
|
37
|
+
components?: _component.Component[]
|
|
38
|
+
dependencies?: Record<string, Dependency[]>
|
|
74
39
|
}
|
|
75
40
|
|
|
76
|
-
export
|
|
41
|
+
export function context (path: string, environment?: string): Promise<Context>
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * as context from './context'
|
|
2
|
+
export * as component from './component'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
export type { Component, Operation } from './component'
|
|
4
|
+
export { Manifest } from './component'
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Feature: Operations declaration
|
|
2
|
-
|
|
3
|
-
Scenario Outline: Operation <channel> entity references
|
|
4
|
-
|
|
5
|
-
Operation IO schemas may reference entity's properties using syntax:
|
|
6
|
-
- `.` - entity's property with the same name
|
|
7
|
-
- '.foo` - entity's property with `foo` name
|
|
8
|
-
|
|
9
|
-
Given I have an entity schema:
|
|
10
|
-
"""
|
|
11
|
-
foo: string
|
|
12
|
-
bar: 1
|
|
13
|
-
"""
|
|
14
|
-
When I declare assignment with:
|
|
15
|
-
"""
|
|
16
|
-
<channel>:
|
|
17
|
-
foo: .
|
|
18
|
-
baz: .bar
|
|
19
|
-
"""
|
|
20
|
-
Then normalized assignment declaration must contain:
|
|
21
|
-
"""
|
|
22
|
-
<channel>:
|
|
23
|
-
type: object
|
|
24
|
-
properties:
|
|
25
|
-
foo:
|
|
26
|
-
type: string
|
|
27
|
-
baz:
|
|
28
|
-
type: number
|
|
29
|
-
default: 1
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
Examples:
|
|
33
|
-
| channel |
|
|
34
|
-
| input |
|
|
35
|
-
| output |
|
|
36
|
-
|
|
37
|
-
Scenario: Prototype property reference
|
|
38
|
-
|
|
39
|
-
Operation IO schemas must be able to reference prototype's entity properties.
|
|
40
|
-
|
|
41
|
-
When I declare assignment with:
|
|
42
|
-
"""
|
|
43
|
-
output:
|
|
44
|
-
id: .
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
Then normalized assignment declaration must contain:
|
|
48
|
-
"""
|
|
49
|
-
output:
|
|
50
|
-
type: object
|
|
51
|
-
properties:
|
|
52
|
-
id:
|
|
53
|
-
$ref: https://schemas.toa.io/0.0.0/definitions#/definitions/id
|
|
54
|
-
"""
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { AssertionError } = require('assert')
|
|
4
|
-
const { generate } = require('randomstring')
|
|
5
|
-
const { dump } = require('@toa.io/yaml')
|
|
6
|
-
const gherkin = require('@toa.io/tomato')
|
|
7
|
-
|
|
8
|
-
const mock = { gherkin }
|
|
9
|
-
|
|
10
|
-
jest.mock('@cucumber/cucumber', () => mock.gherkin)
|
|
11
|
-
require('../manifest')
|
|
12
|
-
|
|
13
|
-
it('should be', () => undefined)
|
|
14
|
-
|
|
15
|
-
/** @type {toa.norm.features.Context} */
|
|
16
|
-
let context
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
const manifest = /** @type {toa.norm.component.Declaration} */ {
|
|
20
|
-
name: 'test',
|
|
21
|
-
namespace: 'features',
|
|
22
|
-
version: '1.0.0',
|
|
23
|
-
entity: null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
context = { manifest }
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe('Given I have an entity schema:', () => {
|
|
30
|
-
const step = gherkin.steps.Gi('I have an entity schema:')
|
|
31
|
-
|
|
32
|
-
it('should be', () => undefined)
|
|
33
|
-
|
|
34
|
-
it('should set entity schema', () => {
|
|
35
|
-
const schema = { foo: 'string' }
|
|
36
|
-
const yaml = dump(schema)
|
|
37
|
-
|
|
38
|
-
step.call(context, yaml)
|
|
39
|
-
|
|
40
|
-
expect(context.manifest.entity).toStrictEqual({ schema })
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
describe('When I declare {operation} with:', () => {
|
|
45
|
-
const step = gherkin.steps.Wh('I declare {operation} with:')
|
|
46
|
-
|
|
47
|
-
it('should be', () => undefined)
|
|
48
|
-
|
|
49
|
-
it('should declare operation', () => {
|
|
50
|
-
const type = 'assignment'
|
|
51
|
-
const scope = 'changeset'
|
|
52
|
-
const input = {}
|
|
53
|
-
const yaml = dump(input)
|
|
54
|
-
|
|
55
|
-
step.call(context, type, yaml)
|
|
56
|
-
|
|
57
|
-
expect(context.manifest.operations[type]).toStrictEqual({ type, scope })
|
|
58
|
-
})
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
describe('Then normalized {operation} declaration must contain:', () => {
|
|
62
|
-
const step = gherkin.steps.Th('normalized {operation} declaration must contain:')
|
|
63
|
-
|
|
64
|
-
it('should be', () => undefined)
|
|
65
|
-
|
|
66
|
-
it('should throw if does not contain', async () => {
|
|
67
|
-
const type = 'assignment'
|
|
68
|
-
|
|
69
|
-
context.manifest.operations = {
|
|
70
|
-
[type]: {
|
|
71
|
-
type,
|
|
72
|
-
input: { bar: 'number' },
|
|
73
|
-
scope: 'changeset'
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const input = {
|
|
78
|
-
type: 'object',
|
|
79
|
-
properties: {
|
|
80
|
-
bar: { type: 'string' }
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const yaml = dump({ input })
|
|
85
|
-
|
|
86
|
-
await expect(step.call(context, type, yaml)).rejects.toThrow(AssertionError)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should not throw if contain', async () => {
|
|
90
|
-
const type = 'assignment'
|
|
91
|
-
|
|
92
|
-
context.manifest.operations = {
|
|
93
|
-
[type]: {
|
|
94
|
-
type,
|
|
95
|
-
input: { bar: 'number' },
|
|
96
|
-
scope: 'changeset'
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const input = {
|
|
101
|
-
type: 'object',
|
|
102
|
-
properties: {
|
|
103
|
-
bar: { type: 'number' }
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const yaml = dump({ input })
|
|
108
|
-
|
|
109
|
-
await expect(step.call(context, type, yaml)).resolves.not.toThrow()
|
|
110
|
-
})
|
|
111
|
-
})
|
package/features/steps/hooks.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { Before } = require('@cucumber/cucumber')
|
|
4
|
-
|
|
5
|
-
Before(
|
|
6
|
-
/**
|
|
7
|
-
* @this {toa.norm.features.Context}
|
|
8
|
-
*/
|
|
9
|
-
function () {
|
|
10
|
-
this.manifest = {
|
|
11
|
-
name: 'test',
|
|
12
|
-
namespace: 'features',
|
|
13
|
-
version: '1.0.0',
|
|
14
|
-
entity: {
|
|
15
|
-
storage: null
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
})
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const assert = require('node:assert')
|
|
4
|
-
const { join } = require('node:path')
|
|
5
|
-
const { parse, save } = require('@toa.io/yaml')
|
|
6
|
-
const { directory } = require('@toa.io/filesystem')
|
|
7
|
-
const { match } = require('@toa.io/generic')
|
|
8
|
-
|
|
9
|
-
const { component: load } = require('../../src')
|
|
10
|
-
|
|
11
|
-
const { Given, When, Then } = require('@cucumber/cucumber')
|
|
12
|
-
|
|
13
|
-
Given('I have an entity schema:',
|
|
14
|
-
/**
|
|
15
|
-
* @param {string} yaml
|
|
16
|
-
* @this {toa.norm.features.Context}
|
|
17
|
-
*/
|
|
18
|
-
function (yaml) {
|
|
19
|
-
const schema = parse(yaml)
|
|
20
|
-
|
|
21
|
-
this.manifest.entity = { schema }
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
When('I declare {operation} with:',
|
|
25
|
-
/**
|
|
26
|
-
* @param {toa.norm.component.operations.Type} type
|
|
27
|
-
* @param {string} yaml
|
|
28
|
-
* @this {toa.norm.features.Context}
|
|
29
|
-
*/
|
|
30
|
-
function (type, yaml) {
|
|
31
|
-
/** @type {toa.norm.component.Operation} */
|
|
32
|
-
const declaration = parse(yaml)
|
|
33
|
-
|
|
34
|
-
declaration.type = type
|
|
35
|
-
declaration.scope = scope(type)
|
|
36
|
-
|
|
37
|
-
this.manifest.operations = { [type]: declaration }
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
Then('normalized {operation} declaration must contain:',
|
|
41
|
-
/**
|
|
42
|
-
* @param {toa.norm.component.operations.Type} type
|
|
43
|
-
* @param {string} yaml
|
|
44
|
-
* @this {toa.norm.features.Context}
|
|
45
|
-
*/
|
|
46
|
-
async function (type, yaml) {
|
|
47
|
-
const temp = await directory.temp()
|
|
48
|
-
const path = join(temp, 'manifest.toa.yaml')
|
|
49
|
-
|
|
50
|
-
await save(this.manifest, path)
|
|
51
|
-
|
|
52
|
-
const manifest = await load(temp)
|
|
53
|
-
const operation = manifest.operations[type]
|
|
54
|
-
const query = parse(yaml)
|
|
55
|
-
const contains = match(operation, query)
|
|
56
|
-
|
|
57
|
-
assert.equal(contains, true)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @param {toa.norm.component.operations.Type} type
|
|
62
|
-
* @returns {toa.norm.component.operations.Scope}
|
|
63
|
-
*/
|
|
64
|
-
const scope = (type) => {
|
|
65
|
-
return type === 'assignment' ? 'changeset' : 'object'
|
|
66
|
-
}
|