@toa.io/extensions.origins 0.10.0-dev.9 → 0.20.0-alpha.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 +17 -11
- package/readme.md +70 -67
- package/schemas/annotation.cos.yaml +3 -0
- package/schemas/manifest.cos.yaml +4 -0
- package/source/Factory.ts +88 -0
- package/source/annotation.test.ts +150 -0
- package/source/annotation.ts +83 -0
- package/source/extension.test.ts +161 -0
- package/source/extension.ts +60 -0
- package/source/index.ts +2 -0
- package/source/manifest.test.ts +30 -0
- package/source/manifest.ts +11 -0
- package/source/protocols/amqp/.test/aspect.fixtures.js +1 -1
- package/source/protocols/amqp/.test/mock.comq.js +2 -2
- package/source/protocols/amqp/aspect.js +17 -24
- package/source/protocols/amqp/deployment.js +8 -2
- package/source/protocols/http/.aspect/permissions.js +20 -15
- package/source/protocols/http/aspect.js +18 -38
- package/source/protocols/index.ts +16 -0
- package/transpiled/Factory.d.ts +17 -0
- package/transpiled/Factory.js +65 -0
- package/transpiled/Factory.js.map +1 -0
- package/transpiled/annotation.d.ts +10 -0
- package/transpiled/annotation.js +91 -0
- package/transpiled/annotation.js.map +1 -0
- package/transpiled/constants.d.ts +1 -0
- package/transpiled/constants.js +3 -0
- package/transpiled/constants.js.map +1 -0
- package/transpiled/env.d.ts +5 -0
- package/transpiled/env.js +40 -0
- package/transpiled/env.js.map +1 -0
- package/transpiled/extension.d.ts +10 -0
- package/transpiled/extension.js +49 -0
- package/transpiled/extension.js.map +1 -0
- package/transpiled/index.d.ts +2 -0
- package/transpiled/index.js +9 -0
- package/transpiled/index.js.map +1 -0
- package/transpiled/manifest.d.ts +2 -0
- package/transpiled/manifest.js +36 -0
- package/transpiled/manifest.js.map +1 -0
- package/transpiled/protocols/amqp/aspect.d.ts +11 -0
- package/transpiled/protocols/amqp/aspect.js +53 -0
- package/transpiled/protocols/amqp/aspect.js.map +1 -0
- package/transpiled/protocols/amqp/deployment.d.ts +5 -0
- package/transpiled/protocols/amqp/deployment.js +55 -0
- package/transpiled/protocols/amqp/deployment.js.map +1 -0
- package/transpiled/protocols/amqp/id.d.ts +1 -0
- package/transpiled/protocols/amqp/id.js +3 -0
- package/transpiled/protocols/amqp/id.js.map +1 -0
- package/transpiled/protocols/amqp/index.d.ts +5 -0
- package/transpiled/protocols/amqp/index.js +10 -0
- package/transpiled/protocols/amqp/index.js.map +1 -0
- package/transpiled/protocols/amqp/protocols.d.ts +2 -0
- package/transpiled/protocols/amqp/protocols.js +3 -0
- package/transpiled/protocols/amqp/protocols.js.map +1 -0
- package/transpiled/protocols/http/.aspect/permissions.d.ts +7 -0
- package/transpiled/protocols/http/.aspect/permissions.js +52 -0
- package/transpiled/protocols/http/.aspect/permissions.js.map +1 -0
- package/transpiled/protocols/http/aspect.d.ts +11 -0
- package/transpiled/protocols/http/aspect.js +88 -0
- package/transpiled/protocols/http/aspect.js.map +1 -0
- package/transpiled/protocols/http/id.d.ts +1 -0
- package/transpiled/protocols/http/id.js +3 -0
- package/transpiled/protocols/http/id.js.map +1 -0
- package/transpiled/protocols/http/index.d.ts +4 -0
- package/transpiled/protocols/http/index.js +8 -0
- package/transpiled/protocols/http/index.js.map +1 -0
- package/transpiled/protocols/http/protocols.d.ts +2 -0
- package/transpiled/protocols/http/protocols.js +3 -0
- package/transpiled/protocols/http/protocols.js.map +1 -0
- package/transpiled/protocols/index.d.ts +9 -0
- package/transpiled/protocols/index.js +10 -0
- package/transpiled/protocols/index.js.map +1 -0
- package/transpiled/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.json +12 -0
- package/source/.deployment/index.js +0 -5
- package/source/.deployment/uris.js +0 -37
- package/source/.test/constants.js +0 -3
- package/source/.test/deployment.fixtures.js +0 -20
- package/source/.test/factory.fixtures.js +0 -13
- package/source/deployment.js +0 -28
- package/source/deployment.test.js +0 -173
- package/source/factory.js +0 -46
- package/source/factory.test.js +0 -140
- package/source/index.js +0 -9
- package/source/manifest.js +0 -38
- package/source/manifest.test.js +0 -75
- package/source/protocols/amqp/aspect.test.js +0 -119
- package/source/protocols/http/aspect.test.js +0 -239
- package/source/protocols/index.js +0 -6
- package/source/schemas/annotations.cos.yaml +0 -1
- package/source/schemas/index.js +0 -8
- package/source/schemas/manifest.cos.yaml +0 -2
- package/types/amqp.d.ts +0 -9
- package/types/deployment.d.ts +0 -7
- package/types/http.d.ts +0 -28
package/source/factory.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const protocols = require('./protocols')
|
|
4
|
-
const env = require('./env')
|
|
5
|
-
|
|
6
|
-
class Factory {
|
|
7
|
-
/**
|
|
8
|
-
* @param {toa.core.Locator} locator
|
|
9
|
-
* @param {toa.origins.Manifest} manifest
|
|
10
|
-
* @return {toa.core.extensions.Aspect[]}
|
|
11
|
-
*/
|
|
12
|
-
aspect (locator, manifest) {
|
|
13
|
-
env.apply(locator, manifest)
|
|
14
|
-
|
|
15
|
-
return protocols.map((protocol) => this.#createAspect(protocol, manifest))
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @param {object} protocol
|
|
20
|
-
* @param {toa.origins.Manifest} manifest
|
|
21
|
-
* @return {toa.core.extensions.Aspect}
|
|
22
|
-
*/
|
|
23
|
-
#createAspect (protocol, manifest) {
|
|
24
|
-
const protocolManifest = {}
|
|
25
|
-
|
|
26
|
-
let properties
|
|
27
|
-
|
|
28
|
-
// let properties
|
|
29
|
-
|
|
30
|
-
for (const [origin, reference] of Object.entries(manifest)) {
|
|
31
|
-
if (origin[0] === '.') {
|
|
32
|
-
if (origin.substring(1) === protocol.id) properties = reference
|
|
33
|
-
|
|
34
|
-
continue
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const url = new URL(reference)
|
|
38
|
-
|
|
39
|
-
if (protocol.protocols.includes(url.protocol)) protocolManifest[origin] = reference
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return protocol.create(protocolManifest, properties)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
exports.Factory = Factory
|
package/source/factory.test.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
const { Locator } = require('@toa.io/core')
|
|
5
|
-
const { sample, overwrite, letters: { up } } = require('@toa.io/generic')
|
|
6
|
-
|
|
7
|
-
jest.mock('./protocols/http/aspect')
|
|
8
|
-
jest.mock('./protocols/amqp/aspect')
|
|
9
|
-
|
|
10
|
-
const http = require('./protocols/http/aspect')
|
|
11
|
-
const amqp = require('./protocols/amqp/aspect')
|
|
12
|
-
|
|
13
|
-
const fixtures = require('./.test/factory.fixtures')
|
|
14
|
-
const { Factory } = require('../')
|
|
15
|
-
|
|
16
|
-
let factory
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
jest.clearAllMocks()
|
|
20
|
-
|
|
21
|
-
factory = new Factory()
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should create aspects', () => {
|
|
25
|
-
factory.aspect(new Locator(generate(), generate()), fixtures.manifest)
|
|
26
|
-
|
|
27
|
-
const httpManifest = filterManifest(fixtures.manifest, 'http')
|
|
28
|
-
const amqpManifest = filterManifest(fixtures.manifest, 'amqp')
|
|
29
|
-
|
|
30
|
-
expect(http.create).toHaveBeenCalledWith(httpManifest, undefined)
|
|
31
|
-
expect(amqp.create).toHaveBeenCalledWith(amqpManifest, undefined)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('env', () => {
|
|
35
|
-
it('should overwrites URLs from environment', async () => {
|
|
36
|
-
const httpManifest = filterManifest(fixtures.manifest, 'http')
|
|
37
|
-
const key = sample(Object.keys(httpManifest))
|
|
38
|
-
const override = { [key]: 'http://' + generate() }
|
|
39
|
-
const json = JSON.stringify(override)
|
|
40
|
-
const base64 = btoa(json)
|
|
41
|
-
const locator = new Locator(generate(), generate())
|
|
42
|
-
|
|
43
|
-
process.env['TOA_ORIGINS_' + locator.uppercase] = base64
|
|
44
|
-
|
|
45
|
-
factory.aspect(locator, fixtures.manifest)
|
|
46
|
-
|
|
47
|
-
const expected = overwrite(httpManifest, override)
|
|
48
|
-
|
|
49
|
-
expect(http.create.mock.calls[0][0]).toStrictEqual(expected)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
describe('amqp', () => {
|
|
53
|
-
/** @type {toa.origins.Manifest} */
|
|
54
|
-
let amqpManifest
|
|
55
|
-
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
amqpManifest = filterManifest(fixtures.manifest, 'amqp')
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should add credentials from environment', async () => {
|
|
61
|
-
const key = sample(Object.keys(amqpManifest))
|
|
62
|
-
const locator = new Locator(generate(), generate())
|
|
63
|
-
const envPrefix = 'TOA_ORIGINS_' + locator.uppercase + '_' + up(key) + '_'
|
|
64
|
-
const username = generate()
|
|
65
|
-
const password = generate()
|
|
66
|
-
|
|
67
|
-
process.env[envPrefix + 'USERNAME'] = username
|
|
68
|
-
process.env[envPrefix + 'PASSWORD'] = password
|
|
69
|
-
|
|
70
|
-
factory.aspect(locator, amqpManifest)
|
|
71
|
-
|
|
72
|
-
const manifest = amqp.create.mock.calls[0][0]
|
|
73
|
-
const origin = manifest[key]
|
|
74
|
-
const url = new URL(origin)
|
|
75
|
-
|
|
76
|
-
expect(url.username).toStrictEqual(username)
|
|
77
|
-
expect(url.password).toStrictEqual(password)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should add credentials to URLs from environment', async () => {
|
|
81
|
-
const key = sample(Object.keys(amqpManifest))
|
|
82
|
-
const hostname = generate().toLowerCase()
|
|
83
|
-
const override = { [key]: 'amqp://' + hostname }
|
|
84
|
-
const json = JSON.stringify(override)
|
|
85
|
-
const base64 = btoa(json)
|
|
86
|
-
const locator = new Locator(generate(), generate())
|
|
87
|
-
const envPrefix = 'TOA_ORIGINS_' + locator.uppercase + '_' + up(key) + '_'
|
|
88
|
-
const username = generate()
|
|
89
|
-
const password = generate()
|
|
90
|
-
|
|
91
|
-
process.env['TOA_ORIGINS_' + locator.uppercase] = base64
|
|
92
|
-
process.env[envPrefix + 'USERNAME'] = username
|
|
93
|
-
process.env[envPrefix + 'PASSWORD'] = password
|
|
94
|
-
|
|
95
|
-
factory.aspect(locator, fixtures.manifest)
|
|
96
|
-
|
|
97
|
-
const manifest = amqp.create.mock.calls[0][0]
|
|
98
|
-
const origin = manifest[key]
|
|
99
|
-
const url = new URL(origin)
|
|
100
|
-
|
|
101
|
-
expect(url.hostname).toStrictEqual(hostname)
|
|
102
|
-
expect(url.username).toStrictEqual(username)
|
|
103
|
-
expect(url.password).toStrictEqual(password)
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe('http', () => {
|
|
108
|
-
it('should read properties', async () => {
|
|
109
|
-
const properties = {
|
|
110
|
-
'.http': {
|
|
111
|
-
[generate()]: generate()
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const locator = new Locator(generate(), generate())
|
|
116
|
-
const json = JSON.stringify(properties)
|
|
117
|
-
|
|
118
|
-
process.env['TOA_ORIGINS_' + locator.uppercase] = btoa(json)
|
|
119
|
-
|
|
120
|
-
factory.aspect(locator, fixtures.manifest)
|
|
121
|
-
|
|
122
|
-
expect(http.create).toHaveBeenCalledWith(expect.anything(), properties['.http'])
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @param {toa.origins.Manifest} manifest
|
|
129
|
-
* @param {string} protocol
|
|
130
|
-
* @return {toa.origins.Manifest}
|
|
131
|
-
*/
|
|
132
|
-
function filterManifest (manifest, protocol) {
|
|
133
|
-
const result = {}
|
|
134
|
-
|
|
135
|
-
for (const [origin, reference] of Object.entries(manifest)) {
|
|
136
|
-
if (reference.slice(0, protocol.length) === protocol) result[origin] = reference
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return result
|
|
140
|
-
}
|
package/source/index.js
DELETED
package/source/manifest.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { remap, echo, shards } = require('@toa.io/generic')
|
|
4
|
-
const schemas = require('./schemas')
|
|
5
|
-
const protocols = require('./protocols')
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @param {toa.origins.Manifest} manifest
|
|
9
|
-
* @returns {toa.origins.Manifest}
|
|
10
|
-
*/
|
|
11
|
-
function manifest (manifest) {
|
|
12
|
-
if (manifest === null) return {}
|
|
13
|
-
|
|
14
|
-
manifest = remap(manifest, (origin) => echo(origin))
|
|
15
|
-
validate(manifest)
|
|
16
|
-
|
|
17
|
-
for (const url of Object.values(manifest)) {
|
|
18
|
-
const supported = protocols.find((provider) => supports(provider, url))
|
|
19
|
-
|
|
20
|
-
if (supported === undefined) throw new Error(`'${url}' protocol is not supported`)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return manifest
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @param {toa.origins.Manifest} manifest
|
|
28
|
-
*/
|
|
29
|
-
function validate (manifest) {
|
|
30
|
-
manifest = remap(manifest, (value) => shards(value)[0])
|
|
31
|
-
schemas.manifest.validate(manifest)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function supports (provider, url) {
|
|
35
|
-
return provider.protocols.findIndex((protocol) => url.substring(0, protocol.length) === protocol) !== -1
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
exports.manifest = manifest
|
package/source/manifest.test.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
const { PROTOCOLS } = require('./.test/constants')
|
|
5
|
-
|
|
6
|
-
const { manifest } = require('../')
|
|
7
|
-
|
|
8
|
-
it('should be', async () => {
|
|
9
|
-
expect(manifest).toBeInstanceOf(Function)
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
it('should return manifest', async () => {
|
|
13
|
-
const input = { [generate()]: 'http://' + generate() }
|
|
14
|
-
const output = manifest(input)
|
|
15
|
-
|
|
16
|
-
expect(output).toStrictEqual(input)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('should fail if not Record<string, string>', async () => {
|
|
20
|
-
const input = /** @type {toa.origins.Manifest} */ {
|
|
21
|
-
foo: {
|
|
22
|
-
bar: 'dev://null'
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
expect(() => manifest(input)).toThrow()
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should pass if valid', async () => {
|
|
30
|
-
const input = { foo: 'amqp://' + generate() }
|
|
31
|
-
|
|
32
|
-
expect(() => manifest(input)).not.toThrow()
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('should fail if not uri', async () => {
|
|
36
|
-
const input = { [generate()]: generate() }
|
|
37
|
-
|
|
38
|
-
expect(() => manifest(input)).toThrow('must match format')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should throw if protocol is not supported', async () => {
|
|
42
|
-
const input = { foo: 'wat://' + generate() }
|
|
43
|
-
|
|
44
|
-
expect(() => manifest(input)).toThrow('is not supported')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should convert null to {}', async () => {
|
|
48
|
-
const output = manifest(null)
|
|
49
|
-
|
|
50
|
-
expect(output).toStrictEqual({})
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it.each(PROTOCOLS)('should support %s protocol', async (protocol) => {
|
|
54
|
-
const input = { foo: protocol + '//' + generate() }
|
|
55
|
-
|
|
56
|
-
expect(() => manifest(input)).not.toThrow()
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should handle placeholders', async () => {
|
|
60
|
-
const input = { foo: 'http://${FOO}' + generate() + ':${BAR}/' } // eslint-disable-line no-template-curly-in-string
|
|
61
|
-
|
|
62
|
-
expect(() => manifest(input)).not.toThrow()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should handle host shards', async () => {
|
|
66
|
-
const input = { foo: 'http://{0-3}' + generate() }
|
|
67
|
-
|
|
68
|
-
expect(() => manifest(input)).not.toThrow()
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('should handle port shards', async () => {
|
|
72
|
-
const input = { foo: 'http://' + generate() + ':888{0-9}' }
|
|
73
|
-
|
|
74
|
-
expect(() => manifest(input)).not.toThrow()
|
|
75
|
-
})
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
const { Connector } = require('@toa.io/core')
|
|
5
|
-
const { sample } = require('@toa.io/generic')
|
|
6
|
-
|
|
7
|
-
const comq = require('./.test/mock.comq')
|
|
8
|
-
const mock = { comq }
|
|
9
|
-
|
|
10
|
-
jest.mock('comq', () => mock.comq)
|
|
11
|
-
|
|
12
|
-
const fixtures = require('./.test/aspect.fixtures')
|
|
13
|
-
const { create } = require('./aspect')
|
|
14
|
-
|
|
15
|
-
it('should be', async () => {
|
|
16
|
-
expect(create).toBeInstanceOf(Function)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
/** @type {toa.origins.amqp.Aspect} */
|
|
20
|
-
let aspect
|
|
21
|
-
|
|
22
|
-
/** @type {toa.origins.Manifest} */
|
|
23
|
-
let manifest
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
jest.clearAllMocks()
|
|
27
|
-
|
|
28
|
-
manifest = fixtures.manifest
|
|
29
|
-
aspect = create(manifest)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it('should be instance of Connector', async () => {
|
|
33
|
-
expect(aspect).toBeInstanceOf(Connector)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('should expose name', async () => {
|
|
37
|
-
expect(aspect.name).toStrictEqual('amqp')
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should connect', async () => {
|
|
41
|
-
await aspect.open()
|
|
42
|
-
|
|
43
|
-
for (const reference of Object.values(manifest)) {
|
|
44
|
-
expect(comq.connect).toHaveBeenCalledWith(reference)
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('should connect to shards', async () => {
|
|
49
|
-
jest.clearAllMocks()
|
|
50
|
-
|
|
51
|
-
manifest = {
|
|
52
|
-
test: 'amqps://host{0-2}.domain.com'
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
aspect = create(manifest)
|
|
56
|
-
|
|
57
|
-
const shards = []
|
|
58
|
-
|
|
59
|
-
for (let i = 0; i < 3; i++) shards.push(`amqps://host${i}.domain.com`)
|
|
60
|
-
|
|
61
|
-
await aspect.open()
|
|
62
|
-
|
|
63
|
-
expect(comq.connect).toHaveBeenCalledWith(...shards)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('should disconnect', async () => {
|
|
67
|
-
await aspect.open()
|
|
68
|
-
await aspect.close()
|
|
69
|
-
|
|
70
|
-
for (const reference of Object.values(manifest)) {
|
|
71
|
-
const index = comq.connect.mock.calls.findIndex((call) => call[0] === reference)
|
|
72
|
-
const io = await comq.connect.mock.results[index].value
|
|
73
|
-
|
|
74
|
-
expect(io.close).toHaveBeenCalled()
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('invoke', () => {
|
|
79
|
-
/** @type {jest.MockedObject<comq.IO>} */
|
|
80
|
-
let io
|
|
81
|
-
|
|
82
|
-
/** @type {string} */
|
|
83
|
-
let origin
|
|
84
|
-
|
|
85
|
-
let args
|
|
86
|
-
|
|
87
|
-
beforeEach(async () => {
|
|
88
|
-
await aspect.open()
|
|
89
|
-
|
|
90
|
-
const origins = Object.keys(manifest)
|
|
91
|
-
|
|
92
|
-
origin = sample(origins)
|
|
93
|
-
|
|
94
|
-
const reference = manifest[origin]
|
|
95
|
-
const index = comq.connect.mock.calls.findIndex((call) => call[0] === reference)
|
|
96
|
-
|
|
97
|
-
io = await comq.connect.mock.results[index].value
|
|
98
|
-
args = [generate(), generate(), generate()]
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('should be', async () => {
|
|
102
|
-
expect(aspect.invoke).toBeInstanceOf(Function)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it.each(['emit', 'request'])('should %s', async (method) => {
|
|
106
|
-
await aspect.invoke(origin, method, ...args)
|
|
107
|
-
|
|
108
|
-
expect(io[method]).toHaveBeenCalledWith(...args)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it.each(['reply', 'consume', generate()])('should not expose %s',
|
|
112
|
-
async (method) => {
|
|
113
|
-
await expect(aspect.invoke(origin, method)).rejects.toThrow()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('should throw if unknown origin', async () => {
|
|
117
|
-
await expect(aspect.invoke(generate(), 'emit')).rejects.toThrow()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const clone = require('clone-deep')
|
|
4
|
-
const { generate } = require('randomstring')
|
|
5
|
-
const { random } = require('@toa.io/generic')
|
|
6
|
-
const { Connector } = require('@toa.io/core')
|
|
7
|
-
|
|
8
|
-
/** @type {string[]} */
|
|
9
|
-
const protocols = require('../http/protocols')
|
|
10
|
-
|
|
11
|
-
const fixtures = require('./.test/aspect.fixtures')
|
|
12
|
-
const mock = fixtures.mock
|
|
13
|
-
|
|
14
|
-
jest.mock('node-fetch', () => mock.fetch)
|
|
15
|
-
|
|
16
|
-
const { create } = require('./aspect')
|
|
17
|
-
|
|
18
|
-
/** @type {toa.origins.http.Aspect} */ let aspect
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
jest.clearAllMocks()
|
|
22
|
-
|
|
23
|
-
aspect = create(fixtures.manifest)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should be instance of core.Connector', () => {
|
|
27
|
-
expect(aspect).toBeInstanceOf(Connector)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('should have name \'origins\'', () => {
|
|
31
|
-
expect(aspect.name).toStrictEqual('http')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('invoke', () => {
|
|
35
|
-
const path = '/' + generate()
|
|
36
|
-
const headers = { [generate().toLowerCase()]: generate() }
|
|
37
|
-
const body = generate()
|
|
38
|
-
|
|
39
|
-
/** @type {import('node-fetch').RequestInit} */
|
|
40
|
-
const request = { method: 'PATCH', headers, body }
|
|
41
|
-
const name = 'foo'
|
|
42
|
-
const response = { [generate()]: generate() }
|
|
43
|
-
|
|
44
|
-
let call
|
|
45
|
-
let args
|
|
46
|
-
let result
|
|
47
|
-
|
|
48
|
-
beforeEach(async () => {
|
|
49
|
-
jest.clearAllMocks()
|
|
50
|
-
mock.fetch.reset()
|
|
51
|
-
mock.fetch.respond(200, response)
|
|
52
|
-
|
|
53
|
-
result = await aspect.invoke(name, path, clone(request))
|
|
54
|
-
call = mock.fetch.mock.calls[0]
|
|
55
|
-
args = call?.[1]
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('should throw on unknown origin', async () => {
|
|
59
|
-
await expect(() => aspect.invoke('bar', path, request)).rejects.toThrow(/is not defined/)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should resolve URL', async () => {
|
|
63
|
-
jest.clearAllMocks()
|
|
64
|
-
mock.fetch.respond(200, response)
|
|
65
|
-
|
|
66
|
-
const path = 'ok'
|
|
67
|
-
|
|
68
|
-
await aspect.invoke('deep', path, clone(request))
|
|
69
|
-
|
|
70
|
-
expect(mock.fetch.mock.calls[0][0]).toStrictEqual(fixtures.manifest.deep + path)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it.each(protocols)('should throw on absolute URL (%s)',
|
|
74
|
-
async (protocol) => {
|
|
75
|
-
jest.clearAllMocks()
|
|
76
|
-
mock.fetch.respond(200, response)
|
|
77
|
-
|
|
78
|
-
await expect(aspect.invoke('deep', protocol + '//api.domain.com', clone(request)))
|
|
79
|
-
.rejects.toThrow('Absolute URLs are forbidden')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should substitute wildcards', async () => {
|
|
83
|
-
jest.clearAllMocks()
|
|
84
|
-
mock.fetch.respond(200, response)
|
|
85
|
-
|
|
86
|
-
const substitutions = ['foo', 'bar', 443]
|
|
87
|
-
|
|
88
|
-
await aspect.invoke('amazon', path, clone(request), { substitutions })
|
|
89
|
-
|
|
90
|
-
const url = mock.fetch.mock.calls[0][0]
|
|
91
|
-
|
|
92
|
-
expect(url).toStrictEqual('https://foo.bar.amazon.com' + path)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('should not lose query string', async () => {
|
|
96
|
-
jest.clearAllMocks()
|
|
97
|
-
mock.fetch.respond(200, response)
|
|
98
|
-
|
|
99
|
-
const path = generate() + '?foo=' + generate()
|
|
100
|
-
|
|
101
|
-
await aspect.invoke(name, path)
|
|
102
|
-
|
|
103
|
-
const url = mock.fetch.mock.calls[0][0]
|
|
104
|
-
|
|
105
|
-
expect(url).toStrictEqual(fixtures.manifest.foo + '/' + path)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('should not throw if path is not defined', async () => {
|
|
109
|
-
jest.clearAllMocks()
|
|
110
|
-
mock.fetch.respond(200, response)
|
|
111
|
-
|
|
112
|
-
// noinspection JSCheckFunctionSignatures
|
|
113
|
-
expect(() => aspect.invoke(name)).not.toThrow()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
describe('fetch', () => {
|
|
117
|
-
it('should fetch', async () => {
|
|
118
|
-
expect(mock.fetch).toHaveBeenCalledTimes(1)
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('should pass url', () => {
|
|
122
|
-
expect(call[0]).toStrictEqual(fixtures.manifest.foo + path)
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('should pass request', () => {
|
|
126
|
-
expect(args).toStrictEqual(request)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('should return response', async () => {
|
|
130
|
-
const body = await result.json()
|
|
131
|
-
|
|
132
|
-
expect(body).toStrictEqual(response)
|
|
133
|
-
})
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
describe('retry', () => {
|
|
137
|
-
it('should retry', async () => {
|
|
138
|
-
jest.clearAllMocks()
|
|
139
|
-
|
|
140
|
-
const attempts = random(5) + 2
|
|
141
|
-
|
|
142
|
-
for (let i = 1; i < attempts; i++) mock.fetch.respond(500)
|
|
143
|
-
|
|
144
|
-
mock.fetch.respond(200, response)
|
|
145
|
-
|
|
146
|
-
/** @type {toa.origins.http.invocation.Options} */
|
|
147
|
-
const options = {
|
|
148
|
-
retry: { base: 0, retries: attempts }
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
await aspect.invoke(name, path, clone(request), options)
|
|
152
|
-
|
|
153
|
-
expect(mock.fetch).toHaveBeenCalledTimes(attempts)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
describe.each(protocols)('absolute URL', (protocol) => {
|
|
159
|
-
const response = { [generate()]: generate() }
|
|
160
|
-
|
|
161
|
-
it('should request absolute URL', async () => {
|
|
162
|
-
mock.fetch.respond(200, response)
|
|
163
|
-
|
|
164
|
-
const properties = { null: true }
|
|
165
|
-
const url = protocol + '//' + generate()
|
|
166
|
-
const request = { method: 'POST' }
|
|
167
|
-
|
|
168
|
-
aspect = create(fixtures.manifest, properties)
|
|
169
|
-
|
|
170
|
-
await aspect.invoke(url, request)
|
|
171
|
-
|
|
172
|
-
expect(mock.fetch).toHaveBeenCalledWith(url, request)
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
it('should allow if TOA_DEV=1 and no properties', async () => {
|
|
176
|
-
const dev = process.env.TOA_DEV
|
|
177
|
-
|
|
178
|
-
process.env.TOA_DEV = '1'
|
|
179
|
-
|
|
180
|
-
mock.fetch.respond(200, response)
|
|
181
|
-
|
|
182
|
-
const url = protocol + '//' + generate()
|
|
183
|
-
const request = { method: 'POST' }
|
|
184
|
-
|
|
185
|
-
aspect = create(fixtures.manifest)
|
|
186
|
-
|
|
187
|
-
await aspect.invoke(url, request)
|
|
188
|
-
|
|
189
|
-
expect(mock.fetch).toHaveBeenCalledWith(url, request)
|
|
190
|
-
|
|
191
|
-
process.env.TOA_DEV = dev
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it('should throw if URL not allowed', async () => {
|
|
195
|
-
mock.fetch.respond(200, response)
|
|
196
|
-
|
|
197
|
-
const properties = {}
|
|
198
|
-
|
|
199
|
-
aspect = create(fixtures.manifest, properties)
|
|
200
|
-
|
|
201
|
-
const url = protocol + '//' + generate()
|
|
202
|
-
const request = { method: 'POST' }
|
|
203
|
-
|
|
204
|
-
await expect(aspect.invoke(url, request)).rejects.toThrow('is not allowed')
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
it.each([
|
|
208
|
-
[String.raw`/^${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`],
|
|
209
|
-
[String.raw`/${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`]
|
|
210
|
-
])('should allow requests %s', async (expression, url) => {
|
|
211
|
-
mock.fetch.respond(200, response)
|
|
212
|
-
|
|
213
|
-
const properties = { [expression]: true }
|
|
214
|
-
|
|
215
|
-
aspect = create(fixtures.manifest, properties)
|
|
216
|
-
|
|
217
|
-
await expect(aspect.invoke(url)).resolves.not.toThrow()
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
it.each([
|
|
221
|
-
[String.raw`/^${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`],
|
|
222
|
-
[String.raw`/${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`]
|
|
223
|
-
])('should allow requests except %s', async (expression, url) => {
|
|
224
|
-
mock.fetch.respond(200, response)
|
|
225
|
-
|
|
226
|
-
const properties = { null: true, [expression]: false }
|
|
227
|
-
|
|
228
|
-
aspect = create(fixtures.manifest, properties)
|
|
229
|
-
|
|
230
|
-
await expect(aspect.invoke(url)).rejects.toThrow('is not allowed')
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it.each([
|
|
234
|
-
['starts', 'expression/'],
|
|
235
|
-
['ends', '/expression']
|
|
236
|
-
])('should throw if rule does not %s with /', async (_, expression) => {
|
|
237
|
-
expect(() => create(fixtures.manifest, { [expression]: true })).toThrow('is not a regular expression')
|
|
238
|
-
})
|
|
239
|
-
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
~: ref:manifest
|
package/source/schemas/index.js
DELETED
package/types/amqp.d.ts
DELETED