@toa.io/bindings.amqp 0.20.0-dev.9 → 0.20.1-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 CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@toa.io/bindings.amqp",
3
- "version": "0.20.0-dev.9",
3
+ "version": "0.20.1-alpha.0",
4
4
  "description": "Toa AMQP Binding",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
7
- "main": "source/index.js",
8
7
  "repository": {
9
8
  "type": "git",
10
9
  "url": "git+https://github.com/toa-io/toa.git"
@@ -15,15 +14,21 @@
15
14
  "publishConfig": {
16
15
  "access": "public"
17
16
  },
17
+ "main": "transpiled/index.js",
18
+ "types": "transpiled/index.d.ts",
19
+ "dependencies": {
20
+ "@toa.io/console": "0.20.1-alpha.0",
21
+ "@toa.io/core": "0.21.0-alpha.0",
22
+ "@toa.io/generic": "0.20.1-alpha.0",
23
+ "@toa.io/pointer": "0.20.1-alpha.0",
24
+ "comq": "0.10.1"
25
+ },
18
26
  "scripts": {
19
- "test": "echo \"Error: run tests from root\" && exit 1"
27
+ "transpile": "tsc"
20
28
  },
21
- "dependencies": {
22
- "@toa.io/console": "0.20.0-dev.9",
23
- "@toa.io/core": "0.20.0-dev.9",
24
- "@toa.io/generic": "0.20.0-dev.9",
25
- "@toa.io/generics.amqp": "0.20.0-dev.9",
26
- "@toa.io/pointer": "0.20.0-dev.9"
29
+ "jest": {
30
+ "preset": "ts-jest",
31
+ "testEnvironment": "node"
27
32
  },
28
- "gitHead": "4e503ffe4fe6dfab1165e795832d3d4fba711582"
33
+ "gitHead": "ed28dc0d2823022fbb1188bb28b994bc827a4432"
29
34
  }
package/readme.md CHANGED
@@ -1,54 +1,19 @@
1
- # Toa AMQP Binding
1
+ # Toa AMQP binding
2
2
 
3
3
  AMQP asynchronous binding on top of [ComQ](/libraries/comq).
4
4
 
5
- ## Deployment
6
-
7
- AMQP deployment must be declared with
8
- the [Pointer annotation](/libraries/pointer/readme.md#annotation). Either `system` or `default`
9
- pointers must be defined.
10
-
11
- Well-known annotation shortcut `amqp` is available.
12
-
13
- ```yaml
14
- # context.toa.yaml
15
- annotations:
16
- "@toa.io/bindings.amqp":
17
- system: url0 # the runtime
18
- default: url1 # all undeclared
19
- dummies: url2 # namespace-wide
20
- dummies.dummy1: url # component exclusive
21
- ```
22
-
23
- ### Concise Declaration
24
-
25
- Well-known shortcut `amqp` is available. The next two declarations are equivalent:
5
+ ## Annotation
26
6
 
27
7
  ```yaml
28
- # context.toa.yaml
29
- annotations:
30
- "@toa.io/bindings.amqp":
31
- system: url0
32
- dummies: url1
33
- ```
34
-
35
- ```yaml
36
- # context.toa.yaml
37
8
  amqp:
38
- system: url0
39
- dummies: url1
9
+ context:
10
+ .: amqp://com.example.com
11
+ dummies.dummy: amqp://dummmy.example.com
12
+ sources:
13
+ somewhere: amqp://queues.somewhere.com
40
14
  ```
41
15
 
42
- `string` annotation value is considered as `default`. The next two declarations are equivalent:
16
+ Context annotaition is a [Pointer](/libraries/pointer) with `amqp-context` ID.
43
17
 
44
- ```yaml
45
- # context.toa.yaml
46
- annotations:
47
- "@toa.io/bindings.amqp":
48
- default: url1
49
- ```
50
-
51
- ```yaml
52
- # context.toa.yaml
53
- amqp: url1
54
- ```
18
+ Sources annotation is a set of Pointers, declared by components consuming foreign events.
19
+ Each Pointer ID is as follows: `amqp-sources-{source}`.
@@ -0,0 +1,2 @@
1
+ context*: <string>
2
+ sources: <string>
@@ -17,11 +17,6 @@ class Broadcast extends Connector {
17
17
  /** @type {string} */
18
18
  #group
19
19
 
20
- /**
21
- * @param {toa.amqp.Communication} comm
22
- * @param {toa.core.Locator} locator
23
- * @param {string} [group]
24
- */
25
20
  constructor (comm, locator, group) {
26
21
  super()
27
22
 
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ const { assert } = require('comq')
4
+ const { Connector } = require('@toa.io/core')
5
+
6
+ class Communication extends Connector {
7
+ #resolve
8
+
9
+ /** @type {comq.IO} */
10
+ #io
11
+
12
+ constructor (resolve) {
13
+ super()
14
+
15
+ this.#resolve = resolve
16
+ }
17
+
18
+ async open () {
19
+ const references = await this.#resolve()
20
+
21
+ this.#io = await assert(...references)
22
+ }
23
+
24
+ async close () {
25
+ await this.#io?.seal()
26
+ }
27
+
28
+ async dispose () {
29
+ await this.#io?.close()
30
+ }
31
+
32
+ async reply (queue, process) {
33
+ await this.#io.reply(queue, process)
34
+ }
35
+
36
+ async request (queue, request) {
37
+ return this.#io.request(queue, request)
38
+ }
39
+
40
+ async emit (exchange, message, properties) {
41
+ await this.#io.emit(exchange, message, properties)
42
+ }
43
+
44
+ async consume (exchange, group, consumer) {
45
+ await this.#io.consume(exchange, group, consumer)
46
+ }
47
+ }
48
+
49
+ exports.Communication = Communication
@@ -1,4 +1,3 @@
1
1
  'use strict'
2
2
 
3
- exports.PREFIX = 'bindings-amqp'
4
3
  exports.SYSTEM = 'system'
@@ -13,11 +13,6 @@ class Consumer extends Connector {
13
13
  /** @type {toa.amqp.Communication} */
14
14
  #comm
15
15
 
16
- /**
17
- * @param {toa.amqp.Communication} comm
18
- * @param {toa.core.Locator} locator
19
- * @param {string} endpoint
20
- */
21
16
  constructor (comm, locator, endpoint) {
22
17
  super()
23
18
 
@@ -0,0 +1,40 @@
1
+ import { generate } from 'randomstring'
2
+ import { normalize } from './annotation'
3
+
4
+ it('should expand string', async () => {
5
+ const declaration = uri()
6
+ const annotation = normalize(declaration)
7
+
8
+ expect(annotation).toMatchObject({
9
+ context: {
10
+ '.': [declaration]
11
+ }
12
+ })
13
+ })
14
+
15
+ it('should expand context default', async () => {
16
+ const declaration = { context: uri() }
17
+ const annotation = normalize(declaration)
18
+
19
+ expect(annotation).toMatchObject({
20
+ context: {
21
+ '.': [declaration.context]
22
+ }
23
+ })
24
+ })
25
+
26
+ it('should expand context with sources', async () => {
27
+ const declaration = { context: uri(), sources: { foo: [uri()] } }
28
+ const annotation = normalize(declaration)
29
+
30
+ expect(annotation).toMatchObject({
31
+ context: {
32
+ '.': [declaration.context]
33
+ },
34
+ sources: declaration.sources
35
+ })
36
+ })
37
+
38
+ function uri (): string {
39
+ return 'http://host-' + generate()
40
+ }
@@ -0,0 +1,26 @@
1
+ import * as pointer from '@toa.io/pointer'
2
+ import { type URIMap } from '@toa.io/pointer'
3
+
4
+ export function normalize (declaration: string | Declaration): Annotation {
5
+ const annotation = expand(declaration)
6
+ const context = pointer.normalize(annotation.context)
7
+ const sources = pointer.normalize(annotation.sources)
8
+
9
+ return { context, sources }
10
+ }
11
+
12
+ function expand (declaration: string | Declaration): Declaration {
13
+ if (typeof declaration === 'string') return { context: { '.': [declaration] } }
14
+ else if (Array.isArray(declaration)) return { context: { '.': declaration } }
15
+ else return declaration
16
+ }
17
+
18
+ export interface Annotation {
19
+ context: URIMap
20
+ sources?: URIMap
21
+ }
22
+
23
+ export interface Declaration {
24
+ context: string | Record<string, string | string[]>
25
+ sources?: Record<string, string | string[]>
26
+ }
@@ -0,0 +1,112 @@
1
+ import { type Dependency, type Variable } from '@toa.io/operations'
2
+ import { encode, decode } from '@toa.io/generic'
3
+ import { resolveRecord, naming } from '@toa.io/pointer'
4
+ import { type Locator } from '@toa.io/core'
5
+ import { type AnnotationRecord } from '@toa.io/pointer/transpiled/Deployment'
6
+ import { dedupe } from '@toa.io/dns'
7
+ import { type Annotation } from './annotation'
8
+
9
+ export function createDependency (context: Context): Dependency {
10
+ const global: Variable[] = []
11
+ const variables = { global }
12
+
13
+ const contextVariables = createVariables(context)
14
+
15
+ global.push(...contextVariables)
16
+
17
+ return { variables }
18
+ }
19
+
20
+ export async function resolveURIs (locator: Locator): Promise<string[]> {
21
+ if (process.env.TOA_DEV === '1') return ['amqp://developer:secret@localhost']
22
+
23
+ const value = process.env[VARIABLE]
24
+
25
+ if (value === undefined) throw new Error(`Environment variable ${VARIABLE} is not specified`)
26
+
27
+ const map = decode(value)
28
+ const record = resolveRecord(map, locator.id)
29
+
30
+ return await parseRecord(record)
31
+ }
32
+
33
+ function createVariables (context: Context): Variable[] {
34
+ const variables: Variable[] = []
35
+ const uris = encode(context)
36
+
37
+ const contextVariable: Variable = {
38
+ name: VARIABLE,
39
+ value: uris
40
+ }
41
+
42
+ const secrets = createSecrets(context)
43
+
44
+ variables.push(contextVariable, ...secrets)
45
+
46
+ return variables
47
+ }
48
+
49
+ function createSecrets (context: Context): Variable[] {
50
+ const secrets: Variable[] = []
51
+
52
+ for (const key of Object.keys(context)) {
53
+ const keySecrets = createKeySecrets(key)
54
+
55
+ secrets.push(...keySecrets)
56
+ }
57
+
58
+ return secrets
59
+ }
60
+
61
+ function createKeySecrets (key: string): Variable[] {
62
+ const username = createSecretVariable(key, 'username')
63
+ const password = createSecretVariable(key, 'password')
64
+
65
+ return [username, password]
66
+ }
67
+
68
+ function createSecretVariable (key: string, secretKey: string): Variable {
69
+ const varKey = key === '.' ? '' : key
70
+ const varName = naming.nameVariable(ID, varKey, secretKey.toUpperCase())
71
+ const secName = naming.nameSecret(ID, key)
72
+
73
+ return {
74
+ name: varName,
75
+ secret: {
76
+ name: secName,
77
+ key: secretKey
78
+ }
79
+ }
80
+ }
81
+
82
+ async function parseRecord (record: AnnotationRecord): Promise<string[]> {
83
+ const unique = await dedupe(record.references)
84
+ const urls = new Array(unique.length)
85
+ const key = record.key === '.' ? '' : record.key
86
+ const username = readEnv(key, 'USERNAME')
87
+ const password = readEnv(key, 'PASSWORD')
88
+
89
+ for (let i = 0; i < record.references.length; i++) {
90
+ const url = new URL(record.references[i])
91
+
92
+ url.username = username
93
+ url.password = password
94
+
95
+ urls[i] = url.href
96
+ }
97
+
98
+ return urls
99
+ }
100
+
101
+ function readEnv (key: string, name: string): string {
102
+ const variable = naming.nameVariable(ID, key, name)
103
+ const value = process.env[variable]
104
+
105
+ if (value === undefined) throw new Error(variable + ' is not set')
106
+ else return value
107
+ }
108
+
109
+ const ID = 'amqp-context'
110
+ const VARIABLE = 'TOA_AMQP_CONTEXT'
111
+
112
+ type Context = Annotation['context']
@@ -0,0 +1,10 @@
1
+ import { type Dependency } from '@toa.io/operations'
2
+ import { type Declaration, normalize } from './annotation'
3
+ import * as sources from './sources'
4
+ import { type Instance } from './instance'
5
+
6
+ export function deployment (instances: Instance[], declaration: Declaration): Dependency {
7
+ const annotation = normalize(declaration)
8
+
9
+ return sources.createDependency(annotation.sources, instances)
10
+ }
@@ -0,0 +1,3 @@
1
+ import { type context } from '@toa.io/norm'
2
+
3
+ export type Instance = context.Dependency
@@ -0,0 +1,44 @@
1
+ import { createVariables, resolve, type Request } from '@toa.io/pointer'
2
+ import { type Manifest } from '@toa.io/norm'
3
+ import { type Dependency } from '@toa.io/operations'
4
+ import { type Locator } from '@toa.io/core'
5
+ import { type Instance } from './instance'
6
+ import { type Annotation } from './annotation'
7
+
8
+ export function createDependency (sources: Sources, instances: Instance[]): Dependency {
9
+ const requests = []
10
+
11
+ for (const instance of instances) {
12
+ const request = createRequest(instance)
13
+
14
+ if (request !== null) requests.push(request)
15
+ }
16
+
17
+ const variables = createVariables(ID, sources, requests)
18
+
19
+ return { variables }
20
+ }
21
+
22
+ export async function resolveURIs (locator: Locator): Promise<string[]> {
23
+ return await resolve(ID, locator.id)
24
+ }
25
+
26
+ function createRequest (instance: Instance): Request | null {
27
+ const group = instance.locator.label
28
+ const selectors = createSelectors(instance.component)
29
+
30
+ if (selectors === null) return null
31
+ else return { group, selectors }
32
+ }
33
+
34
+ function createSelectors (component: Manifest): string[] | null {
35
+ if (component.receivers === undefined) return null
36
+
37
+ const sources = Object.values(component.receivers).map((receiver) => receiver.source)
38
+
39
+ return sources.filter((source) => source !== undefined) as string[]
40
+ }
41
+
42
+ const ID = 'amqp-sources'
43
+
44
+ type Sources = Annotation['sources']
@@ -0,0 +1,14 @@
1
+ import { type Dependency } from '@toa.io/operations'
2
+ import { merge } from '@toa.io/generic'
3
+ import { type Declaration, normalize } from './deployment/annotation'
4
+ import * as sources from './deployment/sources'
5
+ import * as context from './deployment/context'
6
+ import { type Instance } from './deployment/instance'
7
+
8
+ export function deployment (instances: Instance[], declaration: Declaration): Dependency {
9
+ const annotation = normalize(declaration)
10
+ const contextDependency = context.createDependency(annotation.context)
11
+ const sourcesDependency = sources.createDependency(annotation.sources, instances)
12
+
13
+ return merge(contextDependency, sourcesDependency)
14
+ }
package/source/emitter.js CHANGED
@@ -14,11 +14,6 @@ class Emitter extends Connector {
14
14
  /** @type {toa.amqp.Communication} */
15
15
  #comm
16
16
 
17
- /**
18
- * @param {toa.amqp.Communication} comm
19
- * @param {toa.core.Locator} locator
20
- * @param {string} label
21
- */
22
17
  constructor (comm, locator, label) {
23
18
  super()
24
19
 
package/source/factory.js CHANGED
@@ -1,70 +1,62 @@
1
1
  'use strict'
2
2
 
3
3
  const { Locator } = require('@toa.io/core')
4
- const { connector } = require('@toa.io/generics.amqp')
5
4
 
6
5
  const { Producer } = require('./producer')
7
6
  const { Consumer } = require('./consumer')
8
7
  const { Emitter } = require('./emitter')
9
8
  const { Receiver } = require('./receiver')
10
9
  const { Broadcast } = require('./broadcast')
10
+ const context = require('./deployment/context')
11
+ const sources = require('./deployment/sources')
11
12
 
12
- const { SYSTEM, PREFIX } = require('./constants')
13
+ const { SYSTEM } = require('./constants')
14
+ const { Communication } = require('./communication')
13
15
 
14
- /**
15
- * @implements {toa.core.bindings.Factory}
16
- */
17
16
  class Factory {
18
17
  producer (locator, endpoints, component) {
19
- const comm = this.#getCommunication(locator)
18
+ const comm = this.#getContext(locator)
20
19
 
21
20
  return new Producer(comm, locator, endpoints, component)
22
21
  }
23
22
 
24
23
  consumer (locator, endpoint) {
25
- const comm = this.#getCommunication(locator)
24
+ const comm = this.#getContext(locator)
26
25
 
27
26
  return new Consumer(comm, locator, endpoint)
28
27
  }
29
28
 
30
- /**
31
- * @param {toa.core.Locator} locator
32
- * @param {string} label
33
- * @return {Emitter}
34
- */
35
29
  emitter (locator, label) {
36
- const comm = this.#getCommunication(locator)
30
+ const comm = this.#getContext(locator)
37
31
 
38
32
  return new Emitter(comm, locator, label)
39
33
  }
40
34
 
41
- /**
42
- * @param {toa.core.Locator} source
43
- * @param {string} label
44
- * @param {string} group
45
- * @param {toa.core.Receiver} receiver
46
- * @return {Receiver}
47
- */
48
- receiver (source, label, group, receiver) {
49
- const comm = this.#getCommunication(source)
35
+ receiver (locator, label, group, receiver) {
36
+ const comm = this.#getSource(locator)
50
37
 
51
38
  return new Receiver(comm, label, group, receiver)
52
39
  }
53
40
 
54
41
  broadcast (name, group) {
55
42
  const locator = new Locator(name, SYSTEM)
56
- const comm = this.#getCommunication(locator)
43
+ const comm = this.#getContext(locator)
57
44
 
58
45
  return new Broadcast(comm, locator, group)
59
46
  }
60
47
 
61
- /**
62
- *
63
- * @param {toa.core.Locator} source
64
- * @return {toa.amqp.Communication}
65
- */
66
- #getCommunication (source) {
67
- return connector(PREFIX, source)
48
+ #getContext (locator) {
49
+ const resolve = async () => context.resolveURIs(locator)
50
+
51
+ return new Communication(resolve)
52
+ }
53
+
54
+ #getSource (locator) {
55
+ const resolve = (locator.namespace === undefined)
56
+ ? async () => sources.resolveURIs(locator)
57
+ : async () => context.resolveURIs(locator)
58
+
59
+ return new Communication(resolve)
68
60
  }
69
61
  }
70
62
 
package/source/index.js CHANGED
@@ -1,13 +1,11 @@
1
1
  'use strict'
2
2
 
3
3
  const { deployment } = require('./deployment')
4
- const { annotation } = require('./annotation')
5
4
  const { Factory } = require('./factory')
6
5
 
7
6
  /** @type {toa.core.bindings.Properties} */
8
7
  const properties = { async: true }
9
8
 
10
9
  exports.properties = properties
11
- exports.annotation = annotation
12
10
  exports.deployment = deployment
13
11
  exports.Factory = Factory
@@ -17,12 +17,6 @@ class Producer extends Connector {
17
17
  /** @type {toa.core.Component} */
18
18
  #component
19
19
 
20
- /**
21
- * @param {toa.amqp.Communication} comm
22
- * @param {toa.core.Locator} locator
23
- * @param {string[]} endpoints
24
- * @param {toa.core.Component} component
25
- */
26
20
  constructor (comm, locator, endpoints, component) {
27
21
  super()
28
22
 
@@ -15,12 +15,6 @@ class Receiver extends Connector {
15
15
  /** @type {toa.core.Receiver} */
16
16
  #receiver
17
17
 
18
- /**
19
- * @param {toa.amqp.Communication} comm
20
- * @param {string} exchange
21
- * @param {string} group
22
- * @param {toa.core.Receiver} receiver
23
- */
24
18
  constructor (comm, exchange, group, receiver) {
25
19
  super()
26
20
 
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./transpiled"
5
+ },
6
+ "include": [
7
+ "source"
8
+ ],
9
+ "exclude": [
10
+ "**/*.test.ts"
11
+ ]
12
+ }
@@ -1,5 +0,0 @@
1
- 'use strict'
2
-
3
- const { uris } = require('@toa.io/pointer')
4
-
5
- exports.annotation = uris.construct
@@ -1,32 +0,0 @@
1
- 'use strict'
2
-
3
- const pointer = require('@toa.io/pointer')
4
-
5
- const { PREFIX } = require('./constants')
6
-
7
- /**
8
- * @type {toa.deployment.dependency.Constructor}
9
- */
10
- const deployment = (instances, annotation) => {
11
- validate(annotation)
12
-
13
- /** @type {toa.pointer.deployment.Options} */
14
- const options = { prefix: PREFIX }
15
-
16
- return pointer.deployment(instances, annotation, options)
17
- }
18
-
19
- /**
20
- * @param {toa.pointer.URIs} annotation
21
- */
22
- const validate = (annotation) => {
23
- const defined = annotation !== undefined
24
- const defaults = defined && (typeof annotation === 'string' || annotation.default !== undefined)
25
- const correct = defined && (defaults || 'system' in annotation)
26
-
27
- if (!correct) {
28
- throw new Error('AMQP deployment requires either \'system\' or \'default\' pointer annotation')
29
- }
30
- }
31
-
32
- exports.deployment = deployment
package/source/pointer.js DELETED
@@ -1,18 +0,0 @@
1
- 'use strict'
2
-
3
- const { Pointer: Base } = require('@toa.io/pointer')
4
- const { PREFIX } = require('./constants')
5
-
6
- class Pointer extends Base {
7
- /**
8
- * @param {toa.core.Locator} locator
9
- */
10
- constructor (locator) {
11
- super(PREFIX, locator, OPTIONS)
12
- }
13
- }
14
-
15
- /** @type {toa.pointer.Options} */
16
- const OPTIONS = { protocol: 'amqp:' }
17
-
18
- exports.Pointer = Pointer
@@ -1,16 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const mock = { uris: { construct: () => generate() }, Pointer: class {} }
5
-
6
- jest.mock('@toa.io/pointer', () => mock)
7
-
8
- const { annotation } = require('../')
9
-
10
- it('should export annotations', () => {
11
- expect(annotation).toBeDefined()
12
- })
13
-
14
- it('should export connectors.uris construct', () => {
15
- expect(annotation).toStrictEqual(mock.uris.construct)
16
- })
@@ -1,36 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const { random } = require('@toa.io/generic')
5
- const mock = require('@toa.io/mock')
6
-
7
- const { deployment } = require('../')
8
-
9
- /** @type {toa.norm.context.dependencies.Instance[]} */
10
- let instances
11
-
12
- /** @returns {URL} */
13
- const gen = () => new URL('amqp://host-' + generate() + ':' + (random(1000) + 1000))
14
-
15
- beforeEach(() => {
16
- instances = mock.dependencies.instances()
17
- })
18
-
19
- it('should exist', () => {
20
- expect(deployment).toBeDefined()
21
- })
22
-
23
- it('should throw if annotation is not defined', () => {
24
- expect(() => deployment(instances, undefined))
25
- .toThrow('AMQP deployment requires either \'system\' or \'default\' pointer annotation')
26
- })
27
-
28
- it('should throw if \'system\' is not defined', () => {
29
- const url = gen()
30
- const annotation = {}
31
-
32
- for (const instance of instances) annotation[instance.locator.id] = url.href
33
-
34
- expect(() => deployment(instances, annotation))
35
- .toThrow('AMQP deployment requires either \'system\' or \'default\' pointer annotation')
36
- })
@@ -1,190 +0,0 @@
1
- 'use strict'
2
-
3
- // region setup
4
-
5
- const { generate } = require('randomstring')
6
- const { Locator } = require('@toa.io/core')
7
-
8
- jest.mock('@toa.io/generics.amqp')
9
- jest.mock('../source/producer')
10
- jest.mock('../source/consumer')
11
- jest.mock('../source/emitter')
12
- jest.mock('../source/receiver')
13
- jest.mock('../source/broadcast')
14
-
15
- const {
16
- /** @type {jest.MockedFunction<connector>} */
17
- connector
18
- } = require('@toa.io/generics.amqp')
19
-
20
- const {
21
- /** @type {jest.MockedClass<Producer>} */
22
- Producer
23
- } = require('../source/producer')
24
-
25
- const {
26
- /** @type {jest.MockedClass<Consumer>} */
27
- Consumer
28
- } = require('../source/consumer')
29
-
30
- const {
31
- /** @type {jest.MockedClass<Emitter>} */
32
- Emitter
33
- } = require('../source/emitter')
34
-
35
- const {
36
- /** @type {jest.MockedClass<Receiver>} */
37
- Receiver
38
- } = require('../source/receiver')
39
-
40
- const {
41
- /** @type {jest.MockedClass<Broadcast>} */
42
- Broadcast
43
- } = require('../source/broadcast')
44
-
45
- const { Factory } = require('../')
46
-
47
- it('should be', async () => {
48
- expect(Factory).toBeDefined()
49
- })
50
-
51
- /** @type {toa.core.bindings.Factory} */
52
- let factory
53
-
54
- beforeEach(() => {
55
- jest.clearAllMocks()
56
-
57
- factory = new Factory()
58
- })
59
-
60
- const locator = /** @type {toa.core.Locator} */ { name: generate(), namespace: generate() }
61
- const endpoints = [generate(), generate()]
62
- const endpoint = generate()
63
- const label = locator.id + '.' + endpoint
64
- const name = generate()
65
- const group = generate()
66
- const component = /** @type {toa.core.Component} */ {}
67
- const processor = /** @type {toa.core.Receiver} */ {}
68
-
69
- /** @type {jest.MockedObject<toa.amqp.Communication>} */
70
- let comm
71
-
72
- /** @type {toa.core.Connector} */
73
- let producer
74
-
75
- /** @type {toa.core.bindings.Consumer} */
76
- let consumer
77
-
78
- /** @type {toa.core.Connector} */
79
- let receiver
80
-
81
- /** @type {toa.core.bindings.Emitter} */
82
- let emitter
83
-
84
- /** @type {toa.core.bindings.Broadcast} */
85
- let broadcast
86
-
87
- // endregion
88
-
89
- describe.each(['Producer', 'Consumer', 'Emitter', 'Receiver', 'Broadcast'])('%s assets', (classname) => {
90
- const method = classname.toLowerCase()
91
-
92
- it('should be', async () => {
93
- expect(factory[method]).toBeDefined()
94
- })
95
-
96
- beforeEach(() => {
97
- jest.clearAllMocks()
98
-
99
- switch (method) {
100
- case 'producer':
101
- factory.producer(locator, endpoints, component)
102
- break
103
- case 'consumer':
104
- factory.consumer(locator, endpoint)
105
- break
106
- case 'emitter':
107
- factory.emitter(locator, endpoint)
108
- break
109
- case 'receiver':
110
- factory.receiver(locator, endpoint, group, processor)
111
- break
112
- case 'broadcast':
113
- factory.broadcast(name, group)
114
- break
115
- }
116
- })
117
-
118
- if (method !== 'broadcast') {
119
- it('should create Communication', async () => {
120
- expect(connector).toHaveBeenCalledWith('bindings-amqp', locator)
121
- })
122
- }
123
- })
124
-
125
- describe('Producer', () => {
126
- beforeEach(() => {
127
- producer = factory.producer(locator, endpoints, component)
128
- comm = connector.mock.results[0].value
129
- })
130
-
131
- it('should create instance', async () => {
132
- expect(Producer).toHaveBeenCalledWith(comm, locator, endpoints, component)
133
- expect(producer).toStrictEqual(Producer.mock.instances[0])
134
- })
135
- })
136
-
137
- describe('Consumer', () => {
138
- beforeEach(() => {
139
- consumer = factory.consumer(locator, endpoint)
140
- comm = connector.mock.results[0].value
141
- })
142
-
143
- it('should create instance', async () => {
144
- expect(Consumer).toHaveBeenCalledWith(comm, locator, endpoint)
145
- expect(consumer).toStrictEqual(Consumer.mock.instances[0])
146
- })
147
- })
148
-
149
- describe('Emitter', () => {
150
- beforeEach(() => {
151
- emitter = factory.emitter(locator, endpoint)
152
- comm = connector.mock.results[0].value
153
- })
154
-
155
- it('should create instance', async () => {
156
- expect(Emitter).toHaveBeenCalledWith(comm, locator, endpoint)
157
- expect(emitter).toStrictEqual(Emitter.mock.instances[0])
158
- })
159
- })
160
-
161
- describe('Receiver', () => {
162
- beforeEach(() => {
163
- receiver = factory.receiver(locator, label, group, processor)
164
- comm = connector.mock.results[0].value
165
- })
166
-
167
- it('should create instance', async () => {
168
- expect(Receiver).toHaveBeenCalledWith(comm, label, group, processor)
169
- expect(receiver).toStrictEqual(Receiver.mock.instances[0])
170
- })
171
- })
172
-
173
- describe('Broadcast', () => {
174
- beforeEach(() => {
175
- broadcast = factory.broadcast(name, group)
176
- comm = connector.mock.results[0].value
177
- })
178
-
179
- it('should create Locator', async () => {
180
- const locator = Broadcast.mock.calls[0][1]
181
-
182
- expect(locator.namespace).toStrictEqual('system')
183
- expect(locator.name).toStrictEqual(name)
184
- })
185
-
186
- it('should create instance', async () => {
187
- expect(Broadcast).toHaveBeenCalledWith(comm, expect.any(Locator), group)
188
- expect(broadcast).toStrictEqual(Broadcast.mock.instances[0])
189
- })
190
- })
@@ -1,59 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const { Locator } = require('@toa.io/core')
5
- const { encode } = require('@toa.io/generic')
6
-
7
- const { Pointer } = require('../source/pointer')
8
-
9
- /** @type {toa.core.Locator} */
10
- let locator
11
-
12
- /** @type {Pointer} */
13
- let pointer
14
-
15
- const protocol = 'amqp:'
16
-
17
- let url
18
-
19
- beforeAll(() => {
20
- const username = generate()
21
- const password = generate()
22
-
23
- url = new URL('amqps://whatever:5672')
24
-
25
- url.username = username
26
- url.password = password
27
-
28
- process.env.TOA_BINDINGS_AMQP_DEFAULT_USERNAME = username
29
- process.env.TOA_BINDINGS_AMQP_DEFAULT_PASSWORD = password
30
- })
31
-
32
- beforeEach(() => {
33
- const name = generate()
34
- const namespace = generate()
35
- const uris = { default: url.href }
36
- const value = encode(uris)
37
- const key = 'TOA_BINDINGS_AMQP_POINTER'
38
-
39
- process.env[key] = value
40
-
41
- locator = new Locator(name, namespace)
42
- pointer = new Pointer(locator)
43
- })
44
-
45
- it('should be', () => undefined)
46
-
47
- it('should expose reference', () => {
48
- expect(pointer.reference).toStrictEqual(url.href)
49
- })
50
-
51
- it('should set amqp: protocol on localhost', () => {
52
- process.env.TOA_DEV = '1'
53
-
54
- pointer = new Pointer(locator)
55
-
56
- expect(pointer.protocol).toStrictEqual(protocol)
57
-
58
- delete process.env.TOA_DEV
59
- })