@toa.io/extensions.origins 0.20.0-dev.31 → 0.20.0-dev.34
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 -10
- package/readme.md +70 -68
- package/schemas/annotation.cos.yaml +3 -0
- package/schemas/manifest.cos.yaml +4 -0
- package/source/Factory.ts +80 -0
- package/source/annotation.test.ts +150 -0
- package/source/annotation.ts +85 -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/aspect.js +15 -22
- package/source/protocols/http/.aspect/permissions.js +13 -10
- package/source/protocols/http/aspect.js +16 -37
- package/source/protocols/index.ts +16 -0
- package/transpiled/Factory.d.ts +17 -0
- package/transpiled/Factory.js +57 -0
- package/transpiled/Factory.js.map +1 -0
- package/transpiled/annotation.d.ts +10 -0
- package/transpiled/annotation.js +92 -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 +9 -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 +6 -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 +10 -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/tsconfig.json +12 -0
- package/source/.credentials.js +0 -20
- 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 -41
- package/source/deployment.test.js +0 -175
- package/source/factory.js +0 -44
- package/source/factory.test.js +0 -140
- package/source/index.js +0 -9
- package/source/manifest.js +0 -41
- package/source/manifest.test.js +0 -76
- package/source/protocols/amqp/aspect.test.js +0 -119
- package/source/protocols/http/.aspect/permissions.test.js +0 -23
- package/source/protocols/http/aspect.test.js +0 -220
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/protocols/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;;;;;;AAGZ,kDAAyB;AACzB,kDAAyB;AAGZ,QAAA,SAAS,GAAe,CAAC,cAAI,EAAE,cAAI,CAAC,CAAA"}
|
package/tsconfig.json
ADDED
package/source/.credentials.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {string} reference
|
|
5
|
-
*/
|
|
6
|
-
function check (reference) {
|
|
7
|
-
if (typeof reference !== 'string') return // aspect properties object
|
|
8
|
-
|
|
9
|
-
let url
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
url = new URL(reference)
|
|
13
|
-
} catch {
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (url.username !== '' || url.password !== '') throw new Error('Origins must not contain credentials. Please use environment secrets instead.')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
exports.check = check
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { PREFIX } = require('../constants')
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @param {toa.norm.context.dependencies.Instance[]} instances
|
|
7
|
-
* @param {toa.origins.Annotations} annotations
|
|
8
|
-
* @returns {toa.deployment.dependency.Variables}
|
|
9
|
-
*/
|
|
10
|
-
function uris (instances, annotations) {
|
|
11
|
-
const variables = {}
|
|
12
|
-
|
|
13
|
-
for (const [id, annotation] of Object.entries(annotations)) {
|
|
14
|
-
const component = instances.find((instance) => instance.locator.id === id)
|
|
15
|
-
|
|
16
|
-
if (component === undefined) throw new Error(`Origins annotations error: component '${id}' is not found`)
|
|
17
|
-
|
|
18
|
-
for (const origin of Object.keys(annotation)) {
|
|
19
|
-
const properties = origin[0] === '.'
|
|
20
|
-
|
|
21
|
-
if (!properties && !(origin in component.manifest)) {
|
|
22
|
-
throw new Error(`Origins annotations error: component '${id}' doesn't have '${origin}' origin`)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const name = PREFIX + component.locator.uppercase
|
|
27
|
-
const json = JSON.stringify(annotation)
|
|
28
|
-
const value = btoa(json)
|
|
29
|
-
const variable = { name, value }
|
|
30
|
-
|
|
31
|
-
variables[component.locator.label] = [variable]
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return variables
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
exports.uris = uris
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
const { Locator } = require('@toa.io/core')
|
|
5
|
-
const { random, sample } = require('@toa.io/generic')
|
|
6
|
-
|
|
7
|
-
const { PROTOCOLS } = require('./constants')
|
|
8
|
-
|
|
9
|
-
const component = () => ({
|
|
10
|
-
locator: new Locator(generate(), generate()),
|
|
11
|
-
manifest: { [generate()]: sample(PROTOCOLS) + '//' + generate() }
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
const components = () => {
|
|
15
|
-
const length = random(20) + 10
|
|
16
|
-
|
|
17
|
-
return Array.from({ length }, component)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
exports.components = components
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
|
|
5
|
-
/** @type {Record<string, string>} */
|
|
6
|
-
const manifest = {
|
|
7
|
-
[generate()]: 'https://toa.io',
|
|
8
|
-
[generate()]: 'https://api.domain.com',
|
|
9
|
-
[generate()]: 'amqp://localhost',
|
|
10
|
-
[generate()]: 'amqps://localhost'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
exports.manifest = manifest
|
package/source/deployment.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { merge } = require('@toa.io/generic')
|
|
4
|
-
const schemas = require('./schemas')
|
|
5
|
-
const protocols = require('./protocols')
|
|
6
|
-
const create = require('./.deployment')
|
|
7
|
-
const credentials = require('./.credentials')
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {toa.norm.context.dependencies.Instance[]} instances
|
|
11
|
-
* @param {toa.origins.Annotations} annotations
|
|
12
|
-
* @returns {toa.deployment.dependency.Declaration}
|
|
13
|
-
*/
|
|
14
|
-
function deployment (instances, annotations = {}) {
|
|
15
|
-
validate(annotations)
|
|
16
|
-
|
|
17
|
-
const uris = create.uris(instances, annotations)
|
|
18
|
-
const variables = { ...uris }
|
|
19
|
-
|
|
20
|
-
protocols.reduce((variables, provider) => {
|
|
21
|
-
const specifics = provider.deployment?.(instances)
|
|
22
|
-
|
|
23
|
-
return merge(variables, specifics)
|
|
24
|
-
}, variables)
|
|
25
|
-
|
|
26
|
-
return { variables }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @param {toa.origins.Annotations} annotations
|
|
31
|
-
* @return {void}
|
|
32
|
-
*/
|
|
33
|
-
function validate (annotations) {
|
|
34
|
-
schemas.annotations.validate(annotations)
|
|
35
|
-
|
|
36
|
-
for (const component of Object.values(annotations)) {
|
|
37
|
-
Object.values(component).forEach(credentials.check)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
exports.deployment = deployment
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { generate } = require('randomstring')
|
|
4
|
-
const { sample, letters: { up } } = require('@toa.io/generic')
|
|
5
|
-
|
|
6
|
-
const fixtures = require('./.test/deployment.fixtures')
|
|
7
|
-
const { deployment } = require('../')
|
|
8
|
-
|
|
9
|
-
it('should be', async () => {
|
|
10
|
-
expect(deployment).toBeInstanceOf(Function)
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
/** @type {toa.norm.context.dependencies.Instance[]} */
|
|
14
|
-
let components
|
|
15
|
-
|
|
16
|
-
/** @type {string} */
|
|
17
|
-
let origin
|
|
18
|
-
|
|
19
|
-
/** @type {toa.norm.context.dependencies.Instance} */
|
|
20
|
-
let component
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
components = fixtures.components()
|
|
24
|
-
|
|
25
|
-
component = sample(components)
|
|
26
|
-
|
|
27
|
-
const origins = Object.keys(component.manifest)
|
|
28
|
-
|
|
29
|
-
origin = sample(origins)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
describe('validation', () => {
|
|
33
|
-
it('should throw on annotation component mismatch', async () => {
|
|
34
|
-
const id = generate()
|
|
35
|
-
|
|
36
|
-
const annotations = {
|
|
37
|
-
[id]: {
|
|
38
|
-
[origin]: 'dev://' + generate()
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
expect(() => deployment(components, annotations))
|
|
43
|
-
.toThrow(`Origins annotations error: component '${id}' is not found`)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it('should throw on annotation origin mismatch', async () => {
|
|
47
|
-
const id = component.locator.id
|
|
48
|
-
const origin = generate()
|
|
49
|
-
|
|
50
|
-
const annotations = {
|
|
51
|
-
[id]: {
|
|
52
|
-
[origin]: 'dev://' + generate()
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
expect(() => deployment(components, annotations))
|
|
57
|
-
.toThrow(`Origins annotations error: component '${id}' doesn't have '${origin}' origin`)
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('should throw if annotation is not valid', async () => {
|
|
61
|
-
const annotations = /** @type {toa.origins.Annotations} */ { [component.locator.id]: generate() }
|
|
62
|
-
|
|
63
|
-
expect(() => deployment(components, annotations)).toThrow('must be object')
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should create variables', () => {
|
|
68
|
-
const value = 'dev://' + generate()
|
|
69
|
-
|
|
70
|
-
/** @type {toa.origins.Annotations} */
|
|
71
|
-
const annotations = {
|
|
72
|
-
[component.locator.id]: {
|
|
73
|
-
[origin]: value
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const output = deployment(components, annotations)
|
|
78
|
-
|
|
79
|
-
expect(output.variables).not.toBeUndefined()
|
|
80
|
-
|
|
81
|
-
const variables = output.variables[component.locator.label]
|
|
82
|
-
const varName = 'TOA_ORIGINS_' + component.locator.uppercase
|
|
83
|
-
const variable = findVariable(variables, varName)
|
|
84
|
-
|
|
85
|
-
expect(variable).toBeDefined()
|
|
86
|
-
|
|
87
|
-
const json = JSON.stringify(annotations[component.locator.id])
|
|
88
|
-
const base64 = btoa(json)
|
|
89
|
-
|
|
90
|
-
expect(variable.value).toStrictEqual(base64)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it.each(['http', 'amqp'])('should throw if %s annotation contains credentials',
|
|
94
|
-
async (protocol) => {
|
|
95
|
-
/** @type {toa.origins.Annotations} */
|
|
96
|
-
const annotations = {
|
|
97
|
-
[component.locator.id]: {
|
|
98
|
-
[origin]: protocol + '://dev:sec@host-' + generate()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
expect(() => deployment(components, annotations)).toThrow('Origins must not contain credentials')
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
describe('amqp', () => {
|
|
106
|
-
beforeEach(() => {
|
|
107
|
-
const amqpComponents = components.filter(
|
|
108
|
-
(component) => {
|
|
109
|
-
const origin = Object.keys(component.manifest)
|
|
110
|
-
const url = new URL(component.manifest[origin])
|
|
111
|
-
|
|
112
|
-
return url.protocol === 'amqp:' || url.protocol === 'amqps:'
|
|
113
|
-
}
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
component = sample(amqpComponents)
|
|
117
|
-
|
|
118
|
-
const origins = Object.keys(component.manifest)
|
|
119
|
-
|
|
120
|
-
origin = sample(origins)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('should create credential secrets', () => {
|
|
124
|
-
/** @type {toa.origins.Annotations} */
|
|
125
|
-
const annotations = {
|
|
126
|
-
[component.locator.id]: {
|
|
127
|
-
[origin]: 'amqps://whatever'
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const output = deployment(components, annotations)
|
|
132
|
-
const variables = output.variables[component.locator.label]
|
|
133
|
-
|
|
134
|
-
expect(variables).toBeDefined()
|
|
135
|
-
|
|
136
|
-
const envPrefix = `TOA_ORIGINS_${component.locator.uppercase}_${up(origin)}_`
|
|
137
|
-
const secretName = `toa-origins-${component.locator.label}-${origin}`
|
|
138
|
-
|
|
139
|
-
for (const property of ['username', 'password']) {
|
|
140
|
-
const variableName = envPrefix + up(property)
|
|
141
|
-
const variable = findVariable(variables, variableName)
|
|
142
|
-
|
|
143
|
-
expect(variable).toBeDefined()
|
|
144
|
-
|
|
145
|
-
expect(variable.secret).toStrictEqual({
|
|
146
|
-
name: secretName,
|
|
147
|
-
key: property
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
describe('http', () => {
|
|
154
|
-
it('should not throw on properties', async () => {
|
|
155
|
-
const annotations = {
|
|
156
|
-
[component.locator.id]: {
|
|
157
|
-
'.http': {
|
|
158
|
-
null: true
|
|
159
|
-
},
|
|
160
|
-
[origin]: 'amqps://whatever'
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
expect(() => deployment(components, annotations)).not.toThrow()
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @param {toa.deployment.dependency.Variable[]} variables
|
|
170
|
-
* @param {string} name
|
|
171
|
-
* @returns {toa.deployment.dependency.Variable}
|
|
172
|
-
*/
|
|
173
|
-
function findVariable (variables, name) {
|
|
174
|
-
return variables.find((variable) => variable.name === name)
|
|
175
|
-
}
|
package/source/factory.js
DELETED
|
@@ -1,44 +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
|
-
for (const [origin, reference] of Object.entries(manifest)) {
|
|
29
|
-
if (origin[0] === '.') {
|
|
30
|
-
if (origin.substring(1) === protocol.id) properties = reference
|
|
31
|
-
|
|
32
|
-
continue
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const url = new URL(reference)
|
|
36
|
-
|
|
37
|
-
if (protocol.protocols.includes(url.protocol)) protocolManifest[origin] = reference
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return protocol.create(protocolManifest, properties)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
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,41 +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
|
-
const credentials = require('./.credentials')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {toa.origins.Manifest} manifest
|
|
10
|
-
* @returns {toa.origins.Manifest}
|
|
11
|
-
*/
|
|
12
|
-
function manifest (manifest) {
|
|
13
|
-
if (manifest === null) return {}
|
|
14
|
-
|
|
15
|
-
manifest = remap(manifest, (origin) => echo(origin))
|
|
16
|
-
validate(manifest)
|
|
17
|
-
|
|
18
|
-
for (const url of Object.values(manifest)) {
|
|
19
|
-
const supported = protocols.find((provider) => supports(provider, url))
|
|
20
|
-
|
|
21
|
-
if (supported === undefined) throw new Error(`'${url}' protocol is not supported`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return manifest
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @param {toa.origins.Manifest} manifest
|
|
29
|
-
*/
|
|
30
|
-
function validate (manifest) {
|
|
31
|
-
manifest = remap(manifest, (value) => shards(value)[0])
|
|
32
|
-
schemas.manifest.validate(manifest)
|
|
33
|
-
|
|
34
|
-
Object.values(manifest).forEach(credentials.check)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function supports (provider, url) {
|
|
38
|
-
return provider.protocols.findIndex((protocol) => url.substring(0, protocol.length) === protocol) !== -1
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
exports.manifest = manifest
|
package/source/manifest.test.js
DELETED
|
@@ -1,76 +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 throw if protocol is not supported', async () => {
|
|
36
|
-
const input = { foo: 'wat://' + generate() }
|
|
37
|
-
|
|
38
|
-
expect(() => manifest(input)).toThrow('is not supported')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('should convert null to {}', async () => {
|
|
42
|
-
const output = manifest(null)
|
|
43
|
-
|
|
44
|
-
expect(output).toStrictEqual({})
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it.each(PROTOCOLS)('should support %s protocol', async (protocol) => {
|
|
48
|
-
const input = { foo: protocol + '//' + generate() }
|
|
49
|
-
|
|
50
|
-
expect(() => manifest(input)).not.toThrow()
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('should handle placeholders', async () => {
|
|
54
|
-
const input = { foo: 'http://${FOO}' + generate() + ':${BAR}/' } // eslint-disable-line no-template-curly-in-string
|
|
55
|
-
|
|
56
|
-
expect(() => manifest(input)).not.toThrow()
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('should handle host shards', async () => {
|
|
60
|
-
const input = { foo: 'http://{0-3}' + generate() }
|
|
61
|
-
|
|
62
|
-
expect(() => manifest(input)).not.toThrow()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should handle port shards', async () => {
|
|
66
|
-
const input = { foo: 'http://' + generate() + ':888{0-9}' }
|
|
67
|
-
|
|
68
|
-
expect(() => manifest(input)).not.toThrow()
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it.each(['dev:sec', 'dev'])('should throw if url contains credentials (%s)',
|
|
72
|
-
async (credentials) => {
|
|
73
|
-
const input = { foo: `http://${credentials}@${generate()}:888{0-9}` }
|
|
74
|
-
|
|
75
|
-
expect(() => manifest(input)).toThrow('must not contain credentials')
|
|
76
|
-
})
|