@toa.io/norm 0.2.1-dev.1
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/features/component.operations.feature +54 -0
- package/features/steps/.test/manifest.test.js +110 -0
- package/features/steps/.types/context.d.ts +9 -0
- package/features/steps/hooks.js +18 -0
- package/features/steps/manifest.js +66 -0
- package/features/steps/parameters.js +9 -0
- package/package.json +31 -0
- package/src/.component/.expand/bridge.js +9 -0
- package/src/.component/.expand/entity.js +14 -0
- package/src/.component/.expand/events.js +14 -0
- package/src/.component/.expand/extensions.js +10 -0
- package/src/.component/.expand/index.js +15 -0
- package/src/.component/.expand/operations.js +17 -0
- package/src/.component/.expand/receivers.js +16 -0
- package/src/.component/.normalize/events.js +13 -0
- package/src/.component/.normalize/extensions.js +35 -0
- package/src/.component/.normalize/index.js +9 -0
- package/src/.component/.normalize/operations.js +12 -0
- package/src/.component/collapse.js +44 -0
- package/src/.component/defaults.js +25 -0
- package/src/.component/dereference.js +72 -0
- package/src/.component/expand.js +14 -0
- package/src/.component/index.js +17 -0
- package/src/.component/merge.js +55 -0
- package/src/.component/normalize.js +13 -0
- package/src/.component/schema.yaml +222 -0
- package/src/.component/validate.js +40 -0
- package/src/.context/.dependencies/connectors.js +44 -0
- package/src/.context/.dependencies/describe.js +10 -0
- package/src/.context/.dependencies/extensions.js +24 -0
- package/src/.context/.dependencies/index.js +9 -0
- package/src/.context/.dependencies/resolve.js +45 -0
- package/src/.context/complete.js +33 -0
- package/src/.context/dependencies.js +16 -0
- package/src/.context/dereference.js +31 -0
- package/src/.context/expand.js +14 -0
- package/src/.context/index.js +15 -0
- package/src/.context/normalize.js +19 -0
- package/src/.context/schema.yaml +60 -0
- package/src/.context/validate.js +19 -0
- package/src/component.js +59 -0
- package/src/context.js +48 -0
- package/src/index.js +9 -0
- package/src/shortcuts.js +52 -0
- package/test/component/collapse.fixtures.js +134 -0
- package/test/component/collapse.test.js +81 -0
- package/test/component/dereference.fixtures.js +165 -0
- package/test/component/dereference.test.js +31 -0
- package/test/component/dummies/extension/index.js +9 -0
- package/test/component/dummies/extension/package.json +5 -0
- package/test/component/expand.fixtures.js +137 -0
- package/test/component/expand.test.js +17 -0
- package/test/component/normalize.fixtures.js +22 -0
- package/test/component/normalize.test.js +45 -0
- package/test/component/validate.fixtures.js +58 -0
- package/test/component/validate.test.js +250 -0
- package/test/context/complete.fixtures.js +59 -0
- package/test/context/complete.test.js +31 -0
- package/test/context/dereference.fixtures.js +60 -0
- package/test/context/dereference.test.js +24 -0
- package/test/context/expand.fixtures.js +5 -0
- package/test/context/expand.test.js +41 -0
- package/test/context/normalize.fixtures.js +32 -0
- package/test/context/normalize.test.js +34 -0
- package/test/context/validate.fixtures.js +38 -0
- package/test/context/validate.test.js +91 -0
- package/test/shortcuts.fixtures.js +17 -0
- package/test/shortcuts.test.js +92 -0
- package/types/component.d.ts +78 -0
- package/types/context.d.ts +75 -0
- package/types/index.d.ts +5 -0
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
"""
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AssertionError } = require('assert')
|
|
4
|
+
const { dump } = require('@toa.io/yaml')
|
|
5
|
+
const { gherkin } = require('@toa.io/mock')
|
|
6
|
+
|
|
7
|
+
const mock = { gherkin }
|
|
8
|
+
|
|
9
|
+
jest.mock('@cucumber/cucumber', () => mock.gherkin)
|
|
10
|
+
require('../manifest')
|
|
11
|
+
|
|
12
|
+
it('should be', () => undefined)
|
|
13
|
+
|
|
14
|
+
/** @type {toa.norm.features.Context} */
|
|
15
|
+
let context
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
const manifest = {
|
|
19
|
+
name: 'test',
|
|
20
|
+
namespace: 'features',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
entity: null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
context = { manifest }
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('Given I have an entity schema:', () => {
|
|
29
|
+
const step = gherkin.steps.Gi('I have an entity schema:')
|
|
30
|
+
|
|
31
|
+
it('should be', () => undefined)
|
|
32
|
+
|
|
33
|
+
it('should set entity schema', () => {
|
|
34
|
+
const schema = { foo: 'string' }
|
|
35
|
+
const yaml = dump(schema)
|
|
36
|
+
|
|
37
|
+
step.call(context, yaml)
|
|
38
|
+
|
|
39
|
+
expect(context.manifest.entity).toStrictEqual({ schema })
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('When I declare {operation} with:', () => {
|
|
44
|
+
const step = gherkin.steps.Wh('I declare {operation} with:')
|
|
45
|
+
|
|
46
|
+
it('should be', () => undefined)
|
|
47
|
+
|
|
48
|
+
it('should declare operation', () => {
|
|
49
|
+
const type = 'assignment'
|
|
50
|
+
const scope = 'changeset'
|
|
51
|
+
const input = {}
|
|
52
|
+
const yaml = dump(input)
|
|
53
|
+
|
|
54
|
+
step.call(context, type, yaml)
|
|
55
|
+
|
|
56
|
+
expect(context.manifest.operations[type]).toStrictEqual({ type, scope })
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('Then normalized {operation} declaration must contain:', () => {
|
|
61
|
+
const step = gherkin.steps.Th('normalized {operation} declaration must contain:')
|
|
62
|
+
|
|
63
|
+
it('should be', () => undefined)
|
|
64
|
+
|
|
65
|
+
it('should throw if does not contain', async () => {
|
|
66
|
+
const type = 'assignment'
|
|
67
|
+
|
|
68
|
+
context.manifest.operations = {
|
|
69
|
+
[type]: {
|
|
70
|
+
type,
|
|
71
|
+
input: { bar: 'number' },
|
|
72
|
+
scope: 'changeset'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const input = {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {
|
|
79
|
+
bar: { type: 'string' }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const yaml = dump({ input })
|
|
84
|
+
|
|
85
|
+
await expect(step.call(context, type, yaml)).rejects.toThrow(AssertionError)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should not throw if contain', async () => {
|
|
89
|
+
const type = 'assignment'
|
|
90
|
+
|
|
91
|
+
context.manifest.operations = {
|
|
92
|
+
[type]: {
|
|
93
|
+
type,
|
|
94
|
+
input: { bar: 'number' },
|
|
95
|
+
scope: 'changeset'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const input = {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
bar: { type: 'number' }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const yaml = dump({ input })
|
|
107
|
+
|
|
108
|
+
await expect(step.call(context, type, yaml)).resolves.not.toThrow()
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toa.io/norm",
|
|
3
|
+
"version": "0.2.1-dev.1",
|
|
4
|
+
"description": "Toa declarations normalization and validation",
|
|
5
|
+
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
|
+
"homepage": "https://github.com/toa-io/toa#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/toa-io/toa.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/toa-io/toa/issues"
|
|
13
|
+
},
|
|
14
|
+
"main": "src/index.js",
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@toa.io/mock": "*"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@toa.io/core": "*",
|
|
26
|
+
"@toa.io/generic": "*",
|
|
27
|
+
"@toa.io/schema": "*",
|
|
28
|
+
"@toa.io/schemas": "*",
|
|
29
|
+
"@toa.io/yaml": "*"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { expand } = require('@toa.io/schemas')
|
|
4
|
+
|
|
5
|
+
const { resolve } = require('../../shortcuts')
|
|
6
|
+
|
|
7
|
+
function entity (manifest) {
|
|
8
|
+
if (manifest.entity === undefined) return
|
|
9
|
+
|
|
10
|
+
manifest.entity.schema = expand(manifest.entity.schema)
|
|
11
|
+
manifest.entity.storage = resolve(manifest.entity.storage)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.entity = entity
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('../../shortcuts')
|
|
4
|
+
|
|
5
|
+
function events (manifest) {
|
|
6
|
+
if (manifest.events === undefined) return
|
|
7
|
+
|
|
8
|
+
for (const event of Object.values(manifest.events)) {
|
|
9
|
+
if (event.binding !== undefined) event.binding = resolve(event.binding)
|
|
10
|
+
if (event.bridge !== undefined) event.bridge = resolve(event.bridge)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.events = events
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { bridge } = require('./bridge')
|
|
4
|
+
const { entity } = require('./entity')
|
|
5
|
+
const { events } = require('./events')
|
|
6
|
+
const { extensions } = require('./extensions')
|
|
7
|
+
const { operations } = require('./operations')
|
|
8
|
+
const { receivers } = require('./receivers')
|
|
9
|
+
|
|
10
|
+
exports.bridge = bridge
|
|
11
|
+
exports.entity = entity
|
|
12
|
+
exports.events = events
|
|
13
|
+
exports.extensions = extensions
|
|
14
|
+
exports.operations = operations
|
|
15
|
+
exports.receivers = receivers
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { expand } = require('@toa.io/schemas')
|
|
4
|
+
const { resolve } = require('../../shortcuts')
|
|
5
|
+
|
|
6
|
+
function operations (manifest) {
|
|
7
|
+
if (manifest.operations === undefined) return
|
|
8
|
+
|
|
9
|
+
for (const operation of Object.values(manifest.operations)) {
|
|
10
|
+
if (operation.input !== undefined) operation.input = expand(operation.input)
|
|
11
|
+
if (operation.output !== undefined) operation.output = expand(operation.output)
|
|
12
|
+
if (operation.bridge !== undefined) operation.bridge = resolve(operation.bridge)
|
|
13
|
+
if (operation.bindings !== undefined) operation.bindings = operation.bindings.map(resolve)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.operations = operations
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { resolve } = require('../../shortcuts')
|
|
4
|
+
|
|
5
|
+
function receivers (manifest) {
|
|
6
|
+
if (manifest.receivers === undefined) return
|
|
7
|
+
|
|
8
|
+
for (const [locator, receiver] of Object.entries(manifest.receivers)) {
|
|
9
|
+
if (typeof receiver === 'string') manifest.receivers[locator] = { transition: receiver }
|
|
10
|
+
|
|
11
|
+
if (receiver.binding !== undefined) receiver.binding = resolve(receiver.binding)
|
|
12
|
+
if (receiver.bridge !== undefined) receiver.bridge = resolve(receiver.bridge)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.receivers = receivers
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const events = (component) => {
|
|
4
|
+
if (component.events === undefined) return
|
|
5
|
+
|
|
6
|
+
const binding = component.bindings.find((binding) => require(binding).properties.async === true)
|
|
7
|
+
|
|
8
|
+
for (const event of Object.values(component.events)) {
|
|
9
|
+
if (event.binding === undefined) event.binding = binding
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
exports.events = events
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { directory: { find } } = require('@toa.io/filesystem')
|
|
4
|
+
const { resolve } = require('../../shortcuts')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {toa.norm.Component} manifest
|
|
8
|
+
*/
|
|
9
|
+
const extensions = (manifest) => {
|
|
10
|
+
if (manifest.extensions === undefined) return
|
|
11
|
+
|
|
12
|
+
const extensions = manifest.extensions
|
|
13
|
+
|
|
14
|
+
for (let [reference, declaration] of Object.entries(extensions)) {
|
|
15
|
+
let key = resolve(reference)
|
|
16
|
+
|
|
17
|
+
// relative path
|
|
18
|
+
if (key[0] === '.') key = find(key, manifest.path)
|
|
19
|
+
|
|
20
|
+
const extension = require(key)
|
|
21
|
+
|
|
22
|
+
if (extension.manifest !== undefined) {
|
|
23
|
+
declaration = extension.manifest(declaration, manifest)
|
|
24
|
+
|
|
25
|
+
if (declaration === undefined) throw new Error(`Extension '${reference}' didn't return manifest`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
extensions[key] = declaration
|
|
29
|
+
|
|
30
|
+
// shortcut was used
|
|
31
|
+
if (reference !== key) delete extensions[reference]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
exports.extensions = extensions
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const operations = (component) => {
|
|
4
|
+
if (component.operations === undefined) return
|
|
5
|
+
|
|
6
|
+
for (const [endpoint, operation] of Object.entries(component.operations)) {
|
|
7
|
+
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
8
|
+
if (operation.virtual === true) delete component.operations[endpoint]
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exports.operations = operations
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { merge } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
const collapse = (manifest, prototype) => {
|
|
6
|
+
delete manifest.prototype
|
|
7
|
+
|
|
8
|
+
if (prototype.operations) {
|
|
9
|
+
manifest.prototype = { prototype: prototype.prototype, path: prototype.path }
|
|
10
|
+
|
|
11
|
+
const operations = Object.entries(prototype.operations)
|
|
12
|
+
|
|
13
|
+
if (operations.length > 0) {
|
|
14
|
+
if (manifest.operations === undefined) manifest.operations = {}
|
|
15
|
+
|
|
16
|
+
for (let [endpoint, operation] of operations) {
|
|
17
|
+
if (manifest.operations[endpoint] === undefined) manifest.operations[endpoint] = {}
|
|
18
|
+
else {
|
|
19
|
+
const { virtual, ...real } = operation
|
|
20
|
+
|
|
21
|
+
operation = real
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { bridge, binding, ...declaration } = operation
|
|
25
|
+
|
|
26
|
+
merge(manifest.operations[endpoint], declaration)
|
|
27
|
+
|
|
28
|
+
if (bridge !== undefined) {
|
|
29
|
+
if (manifest.prototype.operations === undefined) manifest.prototype.operations = {}
|
|
30
|
+
|
|
31
|
+
manifest.prototype.operations[endpoint] = { bridge }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
delete prototype.entity?.storage
|
|
38
|
+
|
|
39
|
+
const { entity, remotes, events } = prototype
|
|
40
|
+
|
|
41
|
+
merge(manifest, { entity, remotes, events })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
exports.collapse = collapse
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// these defaults are required before validation
|
|
4
|
+
const defaults = (manifest) => {
|
|
5
|
+
if (manifest.prototype === undefined) manifest.prototype = '@toa.io/prototype'
|
|
6
|
+
|
|
7
|
+
if (manifest.bindings === undefined) {
|
|
8
|
+
const local = process.env.TOA_ENV === 'local'
|
|
9
|
+
|
|
10
|
+
manifest.bindings = local
|
|
11
|
+
? ['@toa.io/bindings.amqp']
|
|
12
|
+
: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (manifest.bridge === undefined) manifest.bridge = '@toa.io/bridges.node'
|
|
16
|
+
|
|
17
|
+
if (manifest.entity === null || manifest.entity === undefined) manifest.entity = { storage: null }
|
|
18
|
+
if (manifest.entity.storage === undefined) manifest.entity.storage = '@toa.io/storages.mongodb'
|
|
19
|
+
if (manifest.entity.storage === null) manifest.entity.storage = '@toa.io/storages.null'
|
|
20
|
+
|
|
21
|
+
// TODO: bridge.version()
|
|
22
|
+
if (manifest.version === undefined) manifest.version = '0.0.0'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.defaults = defaults
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { merge } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
const dereference = (manifest) => {
|
|
6
|
+
// schemas
|
|
7
|
+
const property = resolve(manifest.entity.schema.properties)
|
|
8
|
+
|
|
9
|
+
schema(manifest.entity.schema, property)
|
|
10
|
+
|
|
11
|
+
for (const operation of Object.values(manifest.operations)) {
|
|
12
|
+
if (operation.input !== undefined) operation.input = schema(operation.input, property)
|
|
13
|
+
if (operation.output !== undefined) operation.output = schema(operation.output, property)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// forwarding
|
|
17
|
+
for (const operation of Object.values(manifest.operations)) {
|
|
18
|
+
if (operation.forward !== undefined) forward(operation, manifest.operations)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const operation of Object.values(manifest.operations)) {
|
|
22
|
+
delete operation.forwarded
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const resolve = (schema) => (property) => {
|
|
27
|
+
if (schema[property] === undefined) {
|
|
28
|
+
throw new Error(`Referenced property '${property}' is not defined`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return schema[property]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const schema = (object, resolve) => {
|
|
35
|
+
if (object === undefined || typeof object !== 'object') return
|
|
36
|
+
if (object.type === 'string' && object.default?.[0] === '.') return resolve(object.default.substring(1))
|
|
37
|
+
|
|
38
|
+
if (object.type === 'array') {
|
|
39
|
+
object.items = schema(object.items, resolve)
|
|
40
|
+
} else if (object.properties !== undefined) {
|
|
41
|
+
for (const [name, value] of Object.entries(object.properties)) {
|
|
42
|
+
if (value?.type === 'string' && value.default === '.') object.properties[name] = resolve(name)
|
|
43
|
+
else object.properties[name] = schema(value, resolve)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return object
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const types = {
|
|
51
|
+
id: { $ref: 'https://schemas.toa.io/0.0.0/definitions#/definitions/id' }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const forward = (operation, operations) => {
|
|
55
|
+
const target = operations[operation.forward]
|
|
56
|
+
|
|
57
|
+
if (target === undefined) throw new Error(`Referenced operation '${operation.forward}' is not defined`)
|
|
58
|
+
|
|
59
|
+
if (target.forward !== undefined) {
|
|
60
|
+
if (target.forwarded !== true) forward(target, operations)
|
|
61
|
+
|
|
62
|
+
operation.forward = target.forward
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
operation.forwarded = true
|
|
66
|
+
|
|
67
|
+
const { virtual, ...real } = target
|
|
68
|
+
|
|
69
|
+
merge(operation, real)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.dereference = dereference
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { entity, bridge, operations, events, receivers, extensions } = require('./.expand')
|
|
4
|
+
|
|
5
|
+
const expand = (manifest) => {
|
|
6
|
+
entity(manifest)
|
|
7
|
+
bridge(manifest)
|
|
8
|
+
operations(manifest)
|
|
9
|
+
events(manifest)
|
|
10
|
+
receivers(manifest)
|
|
11
|
+
extensions(manifest)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.expand = expand
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { collapse } = require('./collapse')
|
|
4
|
+
const { defaults } = require('./defaults')
|
|
5
|
+
const { dereference } = require('./dereference')
|
|
6
|
+
const { expand } = require('./expand')
|
|
7
|
+
const { merge } = require('./merge')
|
|
8
|
+
const { normalize } = require('./normalize')
|
|
9
|
+
const { validate } = require('./validate')
|
|
10
|
+
|
|
11
|
+
exports.collapse = collapse
|
|
12
|
+
exports.defaults = defaults
|
|
13
|
+
exports.dereference = dereference
|
|
14
|
+
exports.expand = expand
|
|
15
|
+
exports.merge = merge
|
|
16
|
+
exports.normalize = normalize
|
|
17
|
+
exports.validate = validate
|