@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,161 @@
|
|
|
1
|
+
import { generate } from 'randomstring'
|
|
2
|
+
import { encode } from 'msgpackr'
|
|
3
|
+
import { Locator } from '@toa.io/core'
|
|
4
|
+
import { deployment, type Instance } from './extension'
|
|
5
|
+
import type { Annotation, Properties } from './annotation'
|
|
6
|
+
import type { Manifest } from './manifest'
|
|
7
|
+
import type { Dependency } from '@toa.io/operations'
|
|
8
|
+
|
|
9
|
+
const locator = new Locator(generate(), generate())
|
|
10
|
+
const NAMESPACE = locator.namespace.toUpperCase()
|
|
11
|
+
const NAME = locator.name.toUpperCase()
|
|
12
|
+
|
|
13
|
+
it('should deploy pointer variables', async () => {
|
|
14
|
+
const manifest: Manifest = { queue: null }
|
|
15
|
+
const instance = { locator, manifest } as unknown as Instance
|
|
16
|
+
const url = 'amqp://host-' + generate()
|
|
17
|
+
|
|
18
|
+
const annotation: Annotation = {
|
|
19
|
+
[locator.id]: {
|
|
20
|
+
queue: url
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const deploy = deployment([instance], annotation)
|
|
25
|
+
|
|
26
|
+
const expected: Dependency = {
|
|
27
|
+
variables: {
|
|
28
|
+
[locator.label]: expect.arrayContaining([
|
|
29
|
+
{
|
|
30
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE`,
|
|
31
|
+
value: url
|
|
32
|
+
}
|
|
33
|
+
])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect(deploy)
|
|
38
|
+
.toMatchObject(expected)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should deploy default origin', async () => {
|
|
42
|
+
const example = 'http://api.example.com'
|
|
43
|
+
const manifest: Manifest = { example }
|
|
44
|
+
const instance = { locator, manifest } as unknown as Instance
|
|
45
|
+
const annotation: Annotation = {}
|
|
46
|
+
const deploy = deployment([instance], annotation)
|
|
47
|
+
|
|
48
|
+
const expected: Dependency = {
|
|
49
|
+
variables: {
|
|
50
|
+
[locator.label]: expect.arrayContaining([
|
|
51
|
+
{
|
|
52
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_EXAMPLE`,
|
|
53
|
+
value: example
|
|
54
|
+
}
|
|
55
|
+
])
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
expect(deploy)
|
|
60
|
+
.toMatchObject(expected)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should deploy properties', async () => {
|
|
64
|
+
const manifest: Manifest = {}
|
|
65
|
+
const instance = { locator, manifest } as unknown as Instance
|
|
66
|
+
const properties: Properties = {
|
|
67
|
+
'.http': {
|
|
68
|
+
'/^http:\\/\\/\\w+api.example.com/': true
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const annotation: Annotation = {
|
|
73
|
+
[locator.id]: properties
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const deploy = deployment([instance], annotation)
|
|
77
|
+
const value = encode(properties).toString('base64')
|
|
78
|
+
|
|
79
|
+
const expected: Dependency = {
|
|
80
|
+
variables: {
|
|
81
|
+
[locator.label]: expect.arrayContaining([
|
|
82
|
+
{
|
|
83
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}__PROPERTIES`,
|
|
84
|
+
value
|
|
85
|
+
}
|
|
86
|
+
])
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
expect(deploy)
|
|
91
|
+
.toMatchObject(expected)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should deploy properties with null manifest', async () => {
|
|
95
|
+
const manifest: Manifest = null
|
|
96
|
+
const instance = { locator, manifest } as unknown as Instance
|
|
97
|
+
const properties: Properties = {
|
|
98
|
+
'.http': {
|
|
99
|
+
'/^http:\\/\\/\\w+api.example.com/': true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const annotation: Annotation = {
|
|
104
|
+
[locator.id]: properties
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const deploy = deployment([instance], annotation)
|
|
108
|
+
const value = encode(properties).toString('base64')
|
|
109
|
+
|
|
110
|
+
const expected: Dependency = {
|
|
111
|
+
variables: {
|
|
112
|
+
[locator.label]: expect.arrayContaining([
|
|
113
|
+
{
|
|
114
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}__PROPERTIES`,
|
|
115
|
+
value
|
|
116
|
+
}
|
|
117
|
+
])
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
expect(deploy)
|
|
122
|
+
.toMatchObject(expected)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should deploy credentials for amqp', async () => {
|
|
126
|
+
const manifest: Manifest = { queue: null }
|
|
127
|
+
const instance = { locator, manifest } as unknown as Instance
|
|
128
|
+
const url = 'amqp://host-' + generate()
|
|
129
|
+
|
|
130
|
+
const annotation: Annotation = {
|
|
131
|
+
[locator.id]: {
|
|
132
|
+
queue: url
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const deploy = deployment([instance], annotation)
|
|
137
|
+
|
|
138
|
+
const expected: Dependency = {
|
|
139
|
+
variables: {
|
|
140
|
+
[locator.label]: expect.arrayContaining([
|
|
141
|
+
{
|
|
142
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE_USERNAME`,
|
|
143
|
+
secret: {
|
|
144
|
+
name: `toa-origins-${locator.label}-queue`,
|
|
145
|
+
key: 'username'
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE_PASSWORD`,
|
|
150
|
+
secret: {
|
|
151
|
+
name: `toa-origins-${locator.label}-queue`,
|
|
152
|
+
key: 'password'
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
expect(deploy)
|
|
160
|
+
.toMatchObject(expected)
|
|
161
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { encode } from 'msgpackr'
|
|
2
|
+
import { createVariables, type Request } from '@toa.io/pointer'
|
|
3
|
+
import { merge } from '@toa.io/generic'
|
|
4
|
+
import { normalize, split, type Annotation, type Properties, type Origins } from './annotation'
|
|
5
|
+
import { type Manifest, validate } from './manifest'
|
|
6
|
+
import type { Locator } from '@toa.io/core'
|
|
7
|
+
import type { Dependency, Variables } from '@toa.io/operations'
|
|
8
|
+
import type { context } from '@toa.io/norm'
|
|
9
|
+
|
|
10
|
+
export function deployment (instances: Instance[], annotation: Annotation = {}): Dependency {
|
|
11
|
+
normalize(instances, annotation)
|
|
12
|
+
|
|
13
|
+
const variables: Variables = {}
|
|
14
|
+
|
|
15
|
+
for (const instance of instances) {
|
|
16
|
+
const component = annotation[instance.locator.id]
|
|
17
|
+
const { origins, properties } = split(component)
|
|
18
|
+
const instanceVariables = createInstanceVariables(instance, origins)
|
|
19
|
+
const propertiesVariable = createPropertiesVariable(instance.locator, properties)
|
|
20
|
+
|
|
21
|
+
merge(variables, instanceVariables)
|
|
22
|
+
merge(variables, propertiesVariable)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { variables }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function manifest (manifest: Manifest): Manifest {
|
|
29
|
+
validate(manifest)
|
|
30
|
+
|
|
31
|
+
return manifest
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createInstanceVariables (instance: Instance, origins: Origins): Variables {
|
|
35
|
+
if (instance.manifest === null) return {}
|
|
36
|
+
|
|
37
|
+
const label: string = instance.locator.label
|
|
38
|
+
const id = ID_PREFIX + label
|
|
39
|
+
const selectors = Object.keys(instance.manifest)
|
|
40
|
+
const request: Request = { group: label, selectors }
|
|
41
|
+
|
|
42
|
+
return createVariables(id, origins, [request])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function createPropertiesVariable (locator: Locator, properties: Properties): Variables {
|
|
46
|
+
const name = ENV_PREFIX + locator.uppercase + PROPERTIES_SUFFIX
|
|
47
|
+
const value = encode(properties).toString('base64')
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
[locator.label]: [
|
|
51
|
+
{ name, value }
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const ID_PREFIX = 'origins-'
|
|
57
|
+
export const ENV_PREFIX = 'TOA_ORIGINS_'
|
|
58
|
+
export const PROPERTIES_SUFFIX = '__PROPERTIES'
|
|
59
|
+
|
|
60
|
+
export type Instance = context.Dependency<Manifest>
|
package/source/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Manifest, validate } from './manifest'
|
|
2
|
+
|
|
3
|
+
let manifest: Manifest
|
|
4
|
+
|
|
5
|
+
it('should not throw if valid', async () => {
|
|
6
|
+
manifest = {
|
|
7
|
+
one: 'http://localhost',
|
|
8
|
+
two: null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
expect(run).not.toThrow()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('should throw if not a uri', async () => {
|
|
15
|
+
manifest = {
|
|
16
|
+
one: 'not a URI'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
expect(run).toThrow('must match format')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should not throw on null manifest', async () => {
|
|
23
|
+
manifest = null
|
|
24
|
+
|
|
25
|
+
expect(run).not.toThrow()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
function run (): void {
|
|
29
|
+
validate(manifest)
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import * as schemas from '@toa.io/schemas'
|
|
3
|
+
|
|
4
|
+
export function validate (manifest: Manifest): void {
|
|
5
|
+
if (manifest !== null) schema.validate(manifest)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const path = resolve(__dirname, '../schemas/manifest.cos.yaml')
|
|
9
|
+
const schema = schemas.schema(path)
|
|
10
|
+
|
|
11
|
+
export type Manifest = Record<string, string | null> | null
|
|
@@ -2,32 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
const { assert } = require('comq')
|
|
4
4
|
const { Connector } = require('@toa.io/core')
|
|
5
|
-
const
|
|
5
|
+
const protocol = require('./index')
|
|
6
6
|
|
|
7
|
-
const { id } = require('./id')
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @implements {toa.origins.amqp.Aspect}
|
|
11
|
-
*/
|
|
12
7
|
class Aspect extends Connector {
|
|
13
|
-
name = id
|
|
14
|
-
|
|
15
|
-
#
|
|
8
|
+
name = protocol.id
|
|
9
|
+
|
|
10
|
+
#resolve
|
|
16
11
|
|
|
17
12
|
/** @type {Record<string, Partial<comq.IO>>} */
|
|
18
13
|
#origins = {}
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
* @param {toa.origins.Manifest} manifest
|
|
22
|
-
*/
|
|
23
|
-
constructor (manifest) {
|
|
15
|
+
constructor (resolve) {
|
|
24
16
|
super()
|
|
25
17
|
|
|
26
|
-
this.#
|
|
18
|
+
this.#resolve = resolve
|
|
27
19
|
}
|
|
28
20
|
|
|
29
21
|
async open () {
|
|
30
|
-
const
|
|
22
|
+
const cfg = await this.#resolve()
|
|
23
|
+
const promises = Object.entries(cfg.origins).map(this.#open)
|
|
31
24
|
|
|
32
25
|
await Promise.all(promises)
|
|
33
26
|
}
|
|
@@ -39,11 +32,14 @@ class Aspect extends Connector {
|
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
async invoke (origin, method, ...args) {
|
|
35
|
+
if (this.#origins[origin]?.[method] === undefined) {
|
|
36
|
+
throw new Error(`Origin "${origin}" or method "${method}" is undefined`)
|
|
37
|
+
}
|
|
38
|
+
|
|
42
39
|
return this.#origins[origin][method](...args)
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
#open = async ([origin,
|
|
46
|
-
const references = shards(reference)
|
|
42
|
+
#open = async ([origin, references]) => {
|
|
47
43
|
const io = await assert(...references)
|
|
48
44
|
|
|
49
45
|
this.#origins[origin] = restrict(io)
|
|
@@ -67,11 +63,8 @@ function restrict (io) {
|
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
*/
|
|
73
|
-
function create (manifest) {
|
|
74
|
-
return new Aspect(manifest)
|
|
66
|
+
function create (resolve) {
|
|
67
|
+
return new Aspect(resolve)
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
exports.create = create
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { echo } = require('@toa.io/generic')
|
|
4
|
+
const { Connector } = require('@toa.io/core')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
* @implements {toa.origins.http.Permissions}
|
|
7
|
-
*/
|
|
8
|
-
class Permissions {
|
|
9
|
-
#default = process.env.TOA_DEV === '1'
|
|
10
|
-
|
|
6
|
+
class Permissions extends Connector {
|
|
11
7
|
/** @type {RegExp[]} */
|
|
12
8
|
#allowances = []
|
|
13
9
|
|
|
14
10
|
/** @type {RegExp[]} */
|
|
15
11
|
#denials = []
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
#resolve
|
|
14
|
+
|
|
15
|
+
constructor (resolve) {
|
|
16
|
+
super()
|
|
17
|
+
|
|
18
|
+
this.#resolve = resolve
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async open () {
|
|
22
|
+
const { properties } = await this.#resolve()
|
|
23
|
+
|
|
21
24
|
if (properties !== undefined) this.#parse(properties)
|
|
22
25
|
}
|
|
23
26
|
|
|
@@ -1,41 +1,35 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('node-fetch').RequestInit} Request
|
|
5
|
-
* @typedef {import('node-fetch').Response} Response
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
3
|
const fetch = require('node-fetch')
|
|
9
4
|
|
|
10
5
|
const { Connector } = require('@toa.io/core')
|
|
11
6
|
const { retry } = require('@toa.io/generic')
|
|
12
7
|
|
|
13
8
|
const { Permissions } = require('./.aspect/permissions')
|
|
14
|
-
const { id } = require('./id')
|
|
15
9
|
const protocols = require('./protocols')
|
|
10
|
+
const protocol = require('./index')
|
|
16
11
|
|
|
17
|
-
/**
|
|
18
|
-
* @implements {toa.origins.http.Aspect}
|
|
19
|
-
*/
|
|
20
12
|
class Aspect extends Connector {
|
|
21
13
|
/** @readonly */
|
|
22
|
-
name = id
|
|
14
|
+
name = protocol.id
|
|
23
15
|
|
|
24
|
-
|
|
16
|
+
#resolve
|
|
25
17
|
#origins
|
|
26
|
-
|
|
27
|
-
/** @type {toa.origins.http.Permissions} */
|
|
28
18
|
#permissions
|
|
29
19
|
|
|
30
|
-
|
|
31
|
-
* @param {toa.origins.Manifest} manifest
|
|
32
|
-
* @param {toa.origins.http.Permissions} permissions
|
|
33
|
-
*/
|
|
34
|
-
constructor (manifest, permissions) {
|
|
20
|
+
constructor (resolve, permissions) {
|
|
35
21
|
super()
|
|
36
22
|
|
|
37
|
-
this.#
|
|
23
|
+
this.#resolve = resolve
|
|
38
24
|
this.#permissions = permissions
|
|
25
|
+
|
|
26
|
+
this.depends(permissions)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async open () {
|
|
30
|
+
const { origins } = await this.#resolve()
|
|
31
|
+
|
|
32
|
+
this.#origins = origins
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
async invoke (name, path, request, options) {
|
|
@@ -56,23 +50,12 @@ class Aspect extends Connector {
|
|
|
56
50
|
return this.#request(url.href, request, options?.retry)
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
/**
|
|
60
|
-
* @param {string} url
|
|
61
|
-
* @param {Request} request
|
|
62
|
-
* @return {Promise<Response>}
|
|
63
|
-
*/
|
|
64
53
|
async #invokeURL (url, request) {
|
|
65
54
|
if (this.#permissions.test(url) === false) throw new Error(`URL '${url}' is not allowed`)
|
|
66
55
|
|
|
67
56
|
return this.#request(url, request)
|
|
68
57
|
}
|
|
69
58
|
|
|
70
|
-
/**
|
|
71
|
-
* @param {string} url
|
|
72
|
-
* @param {Request} request
|
|
73
|
-
* @param {toa.generic.retry.Options} [options]
|
|
74
|
-
* @return {Promise<Response>}
|
|
75
|
-
*/
|
|
76
59
|
async #request (url, request, options) {
|
|
77
60
|
const call = () => fetch(url, request)
|
|
78
61
|
|
|
@@ -117,14 +100,10 @@ function isAbsoluteURL (path) {
|
|
|
117
100
|
|
|
118
101
|
const PLACEHOLDER = /\*/g
|
|
119
102
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
* @param {toa.origins.http.Properties} [properties]
|
|
123
|
-
*/
|
|
124
|
-
function create (manifest, properties) {
|
|
125
|
-
const permissions = new Permissions(properties)
|
|
103
|
+
function create (resolve) {
|
|
104
|
+
const permissions = new Permissions(resolve)
|
|
126
105
|
|
|
127
|
-
return new Aspect(
|
|
106
|
+
return new Aspect(resolve, permissions)
|
|
128
107
|
}
|
|
129
108
|
|
|
130
109
|
exports.create = create
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { type Resolver } from '../Factory'
|
|
4
|
+
import amqp from './amqp'
|
|
5
|
+
import http from './http'
|
|
6
|
+
import type { extensions } from '@toa.io/core'
|
|
7
|
+
|
|
8
|
+
export const protocols: Protocol[] = [http, amqp]
|
|
9
|
+
|
|
10
|
+
export interface Protocol {
|
|
11
|
+
id: ProtocolID
|
|
12
|
+
protocols: string[]
|
|
13
|
+
create: (resolver: Resolver) => extensions.Aspect
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ProtocolID = 'http' | 'amqp'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type URIMap } from '@toa.io/pointer';
|
|
2
|
+
import type { Locator, extensions } from '@toa.io/core';
|
|
3
|
+
import type { Manifest } from './manifest';
|
|
4
|
+
export declare class Factory implements extensions.Factory {
|
|
5
|
+
aspect(locator: Locator, manifest: Manifest): extensions.Aspect[];
|
|
6
|
+
private createAspect;
|
|
7
|
+
private resolver;
|
|
8
|
+
private getURIs;
|
|
9
|
+
private filterOrigins;
|
|
10
|
+
private readOrigin;
|
|
11
|
+
private getProperties;
|
|
12
|
+
}
|
|
13
|
+
export interface Configuration {
|
|
14
|
+
origins: URIMap;
|
|
15
|
+
properties: Record<string, boolean>;
|
|
16
|
+
}
|
|
17
|
+
export type Resolver = () => Promise<Configuration>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Factory = void 0;
|
|
4
|
+
const msgpackr_1 = require("msgpackr");
|
|
5
|
+
const pointer_1 = require("@toa.io/pointer");
|
|
6
|
+
const generic_1 = require("@toa.io/generic");
|
|
7
|
+
const protocols_1 = require("./protocols");
|
|
8
|
+
const extension_1 = require("./extension");
|
|
9
|
+
class Factory {
|
|
10
|
+
aspect(locator, manifest) {
|
|
11
|
+
return protocols_1.protocols.map((protocol) => this.createAspect(locator, manifest, protocol));
|
|
12
|
+
}
|
|
13
|
+
createAspect(locator, manifest, protocol) {
|
|
14
|
+
const resolver = this.resolver(locator, manifest, protocol);
|
|
15
|
+
return protocol.create(resolver);
|
|
16
|
+
}
|
|
17
|
+
resolver(locator, manifest, protocol) {
|
|
18
|
+
return (0, generic_1.memo)(async () => {
|
|
19
|
+
const uris = await this.getURIs(locator, manifest);
|
|
20
|
+
const allProperties = this.getProperties(locator);
|
|
21
|
+
const origins = this.filterOrigins(uris, protocol.protocols);
|
|
22
|
+
const properties = allProperties['.' + protocol.id] ?? {};
|
|
23
|
+
return { origins, properties };
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async getURIs(locator, manifest) {
|
|
27
|
+
const map = {};
|
|
28
|
+
if (manifest === null)
|
|
29
|
+
return map;
|
|
30
|
+
for (const [name, value] of Object.entries(manifest))
|
|
31
|
+
map[name] = value !== null ? [value] : await this.readOrigin(locator, name);
|
|
32
|
+
return map;
|
|
33
|
+
}
|
|
34
|
+
filterOrigins(uris, protocols) {
|
|
35
|
+
const filtered = {};
|
|
36
|
+
for (const [name, references] of Object.entries(uris)) {
|
|
37
|
+
const url = new URL(references[0]);
|
|
38
|
+
if (protocols.includes(url.protocol))
|
|
39
|
+
filtered[name] = references;
|
|
40
|
+
}
|
|
41
|
+
return filtered;
|
|
42
|
+
}
|
|
43
|
+
async readOrigin(locator, name) {
|
|
44
|
+
const id = extension_1.ID_PREFIX + locator.label;
|
|
45
|
+
return await (0, pointer_1.resolve)(id, name);
|
|
46
|
+
}
|
|
47
|
+
getProperties(locator) {
|
|
48
|
+
const variable = extension_1.ENV_PREFIX + locator.uppercase + extension_1.PROPERTIES_SUFFIX;
|
|
49
|
+
const value = process.env[variable];
|
|
50
|
+
if (value === undefined)
|
|
51
|
+
return {};
|
|
52
|
+
const buffer = Buffer.from(value, 'base64');
|
|
53
|
+
return (0, msgpackr_1.decode)(buffer);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.Factory = Factory;
|
|
57
|
+
//# sourceMappingURL=Factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Instance } from './extension';
|
|
2
|
+
export declare function normalize(instances: Instance[], annotation: Annotation): void;
|
|
3
|
+
export declare function split(component: Component): {
|
|
4
|
+
origins: Origins;
|
|
5
|
+
properties: Properties;
|
|
6
|
+
};
|
|
7
|
+
export type Component = Origins | Properties;
|
|
8
|
+
export type Annotation = Record<string, Component>;
|
|
9
|
+
export type Properties = Partial<Record<'.http', Record<string, boolean>>>;
|
|
10
|
+
export type Origins = Record<string, string | string[]>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.split = exports.normalize = void 0;
|
|
27
|
+
const node_path_1 = require("node:path");
|
|
28
|
+
const schemas = __importStar(require("@toa.io/schemas"));
|
|
29
|
+
const protocols_1 = require("./protocols");
|
|
30
|
+
function normalize(instances, annotation) {
|
|
31
|
+
schema.validate(annotation);
|
|
32
|
+
mergeDefaults(annotation, instances);
|
|
33
|
+
checkProtocols(annotation);
|
|
34
|
+
}
|
|
35
|
+
exports.normalize = normalize;
|
|
36
|
+
function split(component) {
|
|
37
|
+
const origins = {};
|
|
38
|
+
const properties = {};
|
|
39
|
+
for (const [key, value] of Object.entries(component))
|
|
40
|
+
if (key[0] === '.')
|
|
41
|
+
properties[key] = value;
|
|
42
|
+
else
|
|
43
|
+
origins[key] = value;
|
|
44
|
+
return { origins, properties };
|
|
45
|
+
}
|
|
46
|
+
exports.split = split;
|
|
47
|
+
function mergeDefaults(annotation, instances) {
|
|
48
|
+
for (const instance of instances) {
|
|
49
|
+
const component = annotation[instance.locator.id] ?? {};
|
|
50
|
+
annotation[instance.locator.id] = mergeInstance(component, instance);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function mergeInstance(origins, instance) {
|
|
54
|
+
const id = instance.locator.id;
|
|
55
|
+
if (instance.manifest === null)
|
|
56
|
+
return origins;
|
|
57
|
+
for (const [origin, value] of Object.entries(instance.manifest))
|
|
58
|
+
if (origins[origin] === undefined)
|
|
59
|
+
if (value === null)
|
|
60
|
+
throw new Error(`Origin '${origin}' is not defined for '${id}'`);
|
|
61
|
+
else
|
|
62
|
+
origins[origin] = value;
|
|
63
|
+
return origins;
|
|
64
|
+
}
|
|
65
|
+
function checkProtocols(annotation) {
|
|
66
|
+
for (const component of Object.values(annotation)) {
|
|
67
|
+
const { origins } = split(component);
|
|
68
|
+
const urlSets = Object.values(origins);
|
|
69
|
+
for (const urls of urlSets)
|
|
70
|
+
checkURLs(Array.isArray(urls) ? urls : [urls]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function checkURLs(urls) {
|
|
74
|
+
let id = null;
|
|
75
|
+
for (const url of urls) {
|
|
76
|
+
const protocol = resolveProtocol(url);
|
|
77
|
+
if (id === null)
|
|
78
|
+
id = protocol.id;
|
|
79
|
+
else if (id !== protocol.id)
|
|
80
|
+
throw new Error(`Origin has inconsistent protocols: ${id}, ${protocol.id}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function resolveProtocol(reference) {
|
|
84
|
+
const url = new URL(reference);
|
|
85
|
+
for (const protocol of protocols_1.protocols)
|
|
86
|
+
if (protocol.protocols.includes(url.protocol))
|
|
87
|
+
return protocol;
|
|
88
|
+
throw new Error(`Protocol '${url.protocol}' is not supported.`);
|
|
89
|
+
}
|
|
90
|
+
const path = (0, node_path_1.resolve)(__dirname, '../schemas/annotation.cos.yaml');
|
|
91
|
+
const schema = schemas.schema(path);
|
|
92
|
+
//# sourceMappingURL=annotation.js.map
|