@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.
Files changed (95) hide show
  1. package/package.json +17 -10
  2. package/readme.md +70 -68
  3. package/schemas/annotation.cos.yaml +3 -0
  4. package/schemas/manifest.cos.yaml +4 -0
  5. package/source/Factory.ts +80 -0
  6. package/source/annotation.test.ts +150 -0
  7. package/source/annotation.ts +85 -0
  8. package/source/extension.test.ts +161 -0
  9. package/source/extension.ts +60 -0
  10. package/source/index.ts +2 -0
  11. package/source/manifest.test.ts +30 -0
  12. package/source/manifest.ts +11 -0
  13. package/source/protocols/amqp/.test/aspect.fixtures.js +1 -1
  14. package/source/protocols/amqp/aspect.js +15 -22
  15. package/source/protocols/http/.aspect/permissions.js +13 -10
  16. package/source/protocols/http/aspect.js +16 -37
  17. package/source/protocols/index.ts +16 -0
  18. package/transpiled/Factory.d.ts +17 -0
  19. package/transpiled/Factory.js +57 -0
  20. package/transpiled/Factory.js.map +1 -0
  21. package/transpiled/annotation.d.ts +10 -0
  22. package/transpiled/annotation.js +92 -0
  23. package/transpiled/annotation.js.map +1 -0
  24. package/transpiled/constants.d.ts +1 -0
  25. package/transpiled/constants.js +3 -0
  26. package/transpiled/constants.js.map +1 -0
  27. package/transpiled/env.d.ts +5 -0
  28. package/transpiled/env.js +40 -0
  29. package/transpiled/env.js.map +1 -0
  30. package/transpiled/extension.d.ts +10 -0
  31. package/transpiled/extension.js +49 -0
  32. package/transpiled/extension.js.map +1 -0
  33. package/transpiled/index.d.ts +2 -0
  34. package/transpiled/index.js +9 -0
  35. package/transpiled/index.js.map +1 -0
  36. package/transpiled/manifest.d.ts +2 -0
  37. package/transpiled/manifest.js +36 -0
  38. package/transpiled/manifest.js.map +1 -0
  39. package/transpiled/protocols/amqp/aspect.d.ts +9 -0
  40. package/transpiled/protocols/amqp/aspect.js +53 -0
  41. package/transpiled/protocols/amqp/aspect.js.map +1 -0
  42. package/transpiled/protocols/amqp/deployment.d.ts +5 -0
  43. package/transpiled/protocols/amqp/deployment.js +55 -0
  44. package/transpiled/protocols/amqp/deployment.js.map +1 -0
  45. package/transpiled/protocols/amqp/id.d.ts +1 -0
  46. package/transpiled/protocols/amqp/id.js +3 -0
  47. package/transpiled/protocols/amqp/id.js.map +1 -0
  48. package/transpiled/protocols/amqp/index.d.ts +5 -0
  49. package/transpiled/protocols/amqp/index.js +10 -0
  50. package/transpiled/protocols/amqp/index.js.map +1 -0
  51. package/transpiled/protocols/amqp/protocols.d.ts +2 -0
  52. package/transpiled/protocols/amqp/protocols.js +3 -0
  53. package/transpiled/protocols/amqp/protocols.js.map +1 -0
  54. package/transpiled/protocols/http/.aspect/permissions.d.ts +6 -0
  55. package/transpiled/protocols/http/.aspect/permissions.js +52 -0
  56. package/transpiled/protocols/http/.aspect/permissions.js.map +1 -0
  57. package/transpiled/protocols/http/aspect.d.ts +10 -0
  58. package/transpiled/protocols/http/aspect.js +88 -0
  59. package/transpiled/protocols/http/aspect.js.map +1 -0
  60. package/transpiled/protocols/http/id.d.ts +1 -0
  61. package/transpiled/protocols/http/id.js +3 -0
  62. package/transpiled/protocols/http/id.js.map +1 -0
  63. package/transpiled/protocols/http/index.d.ts +4 -0
  64. package/transpiled/protocols/http/index.js +8 -0
  65. package/transpiled/protocols/http/index.js.map +1 -0
  66. package/transpiled/protocols/http/protocols.d.ts +2 -0
  67. package/transpiled/protocols/http/protocols.js +3 -0
  68. package/transpiled/protocols/http/protocols.js.map +1 -0
  69. package/transpiled/protocols/index.d.ts +9 -0
  70. package/transpiled/protocols/index.js +10 -0
  71. package/transpiled/protocols/index.js.map +1 -0
  72. package/tsconfig.json +12 -0
  73. package/source/.credentials.js +0 -20
  74. package/source/.deployment/index.js +0 -5
  75. package/source/.deployment/uris.js +0 -37
  76. package/source/.test/constants.js +0 -3
  77. package/source/.test/deployment.fixtures.js +0 -20
  78. package/source/.test/factory.fixtures.js +0 -13
  79. package/source/deployment.js +0 -41
  80. package/source/deployment.test.js +0 -175
  81. package/source/factory.js +0 -44
  82. package/source/factory.test.js +0 -140
  83. package/source/index.js +0 -9
  84. package/source/manifest.js +0 -41
  85. package/source/manifest.test.js +0 -76
  86. package/source/protocols/amqp/aspect.test.js +0 -119
  87. package/source/protocols/http/.aspect/permissions.test.js +0 -23
  88. package/source/protocols/http/aspect.test.js +0 -220
  89. package/source/protocols/index.js +0 -6
  90. package/source/schemas/annotations.cos.yaml +0 -1
  91. package/source/schemas/index.js +0 -8
  92. package/source/schemas/manifest.cos.yaml +0 -2
  93. package/types/amqp.d.ts +0 -9
  94. package/types/deployment.d.ts +0 -7
  95. package/types/http.d.ts +0 -28
@@ -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.assert).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.assert).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.assert.mock.calls.findIndex((call) => call[0] === reference)
72
- const io = await comq.assert.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.assert.mock.calls.findIndex((call) => call[0] === reference)
96
-
97
- io = await comq.assert.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,23 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const { Permissions } = require('./permissions')
5
-
6
- it('should be', async () => {
7
- expect(Permissions).toBeInstanceOf(Function)
8
- })
9
-
10
- it('should substitute env vars', async () => {
11
- const name = 'FOO_VALUE'
12
- const value = generate()
13
-
14
- process.env[name] = value
15
-
16
- const properties = { '/http:\/\/domain.com\/${FOO_VALUE}/': true }
17
- const permissions = new Permissions(properties)
18
-
19
- expect(permissions.test('http://other.domain.com/')).toStrictEqual(false)
20
- expect(permissions.test('http://domain.com/' + value)).toStrictEqual(true)
21
-
22
- delete process.env[name]
23
- })
@@ -1,220 +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 throw if URL not allowed', async () => {
176
- mock.fetch.respond(200, response)
177
-
178
- const properties = {}
179
-
180
- aspect = create(fixtures.manifest, properties)
181
-
182
- const url = protocol + '//' + generate()
183
- const request = { method: 'POST' }
184
-
185
- await expect(aspect.invoke(url, request)).rejects.toThrow('is not allowed')
186
- })
187
-
188
- it.each([
189
- [String.raw`/^${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`],
190
- [String.raw`/${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`]
191
- ])('should allow requests %s', async (expression, url) => {
192
- mock.fetch.respond(200, response)
193
-
194
- const properties = { [expression]: true }
195
-
196
- aspect = create(fixtures.manifest, properties)
197
-
198
- await expect(aspect.invoke(url)).resolves.not.toThrow()
199
- })
200
-
201
- it.each([
202
- [String.raw`/^${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`],
203
- [String.raw`/${protocol}\/\/api.\S+.com/`, `${protocol}//api.${generate()}.com/path/to`]
204
- ])('should allow requests except %s', async (expression, url) => {
205
- mock.fetch.respond(200, response)
206
-
207
- const properties = { null: true, [expression]: false }
208
-
209
- aspect = create(fixtures.manifest, properties)
210
-
211
- await expect(aspect.invoke(url)).rejects.toThrow('is not allowed')
212
- })
213
-
214
- it.each([
215
- ['starts', 'expression/'],
216
- ['ends', '/expression']
217
- ])('should throw if rule does not %s with /', async (_, expression) => {
218
- expect(() => create(fixtures.manifest, { [expression]: true })).toThrow('is not a regular expression')
219
- })
220
- })
@@ -1,6 +0,0 @@
1
- 'use strict'
2
-
3
- const http = require('./http')
4
- const amqp = require('./amqp')
5
-
6
- module.exports = [http, amqp]
@@ -1 +0,0 @@
1
- ~: ref:manifest
@@ -1,8 +0,0 @@
1
- 'use strict'
2
-
3
- const schemas = require('@toa.io/schemas')
4
-
5
- const namespace = schemas.namespace(__dirname)
6
-
7
- exports.manifest = namespace.schema('manifest')
8
- exports.annotations = namespace.schema('annotations')
@@ -1,2 +0,0 @@
1
- /^\./: <boolean>
2
- /^[a-zA-Z0-9]{1,32}$/: string
package/types/amqp.d.ts DELETED
@@ -1,9 +0,0 @@
1
- import * as _extensions from '@toa.io/core/types/extensions'
2
-
3
- declare namespace toa.origins.amqp {
4
-
5
- interface Aspect extends _extensions.Aspect {
6
- invoke(origin: string, method: string, ...args: any[]): Promise<any>
7
- }
8
-
9
- }
@@ -1,7 +0,0 @@
1
- declare namespace toa.origins {
2
-
3
- type Manifest = Record<string, string>
4
-
5
- type Annotations = Record<string, Manifest>
6
-
7
- }
package/types/http.d.ts DELETED
@@ -1,28 +0,0 @@
1
- import * as fetch from 'node-fetch'
2
- import * as _extensions from '@toa.io/core/types/extensions'
3
- import * as _retry from '@toa.io/generic/types/retry'
4
-
5
- declare namespace toa.origins.http {
6
-
7
- namespace invocation {
8
- type Options = {
9
- substitutions?: string[]
10
- retry?: _retry.Options
11
- }
12
- }
13
-
14
- type Properties = Record<string | null, boolean>
15
-
16
- interface Permissions {
17
- test(url: string): boolean
18
- }
19
-
20
- interface Aspect extends _extensions.Aspect {
21
- invoke(origin: string, path: string, request?: fetch.RequestInit, options?: invocation.Options): Promise<fetch.Response>
22
-
23
- invoke(url: string, request?: fetch.RequestInit): Promise<fetch.Response>
24
- }
25
-
26
- }
27
-
28
- export type Aspect = toa.origins.http.Aspect