@ti-engine/core 1.0.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.
@@ -0,0 +1,163 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const _ = require( "lodash" );
7
+ const tools = require( "#tools" );
8
+ const logger = require( "#logger" );
9
+ const config = require( "#config" );
10
+ const gcloud = require( "#gcloud-integration" );
11
+
12
+ /**
13
+ * @typedef {Object} LogEntry
14
+ * @property {string} _id Unique identifier that can be used to identify the document in a NoSQL database.
15
+ * @property {TiLogSeverity} severity The log severity level.
16
+ * @property {string} thread The categorization of the log message.
17
+ * @property {string} reporter
18
+ * @property {string} message The actual log message.
19
+ * @property {number} timestamp The timestamp of the log entry in UTC time.
20
+ * @property {Object} data Additional JSON data to accompany the message.
21
+ */
22
+
23
+ /**
24
+ * Used to create and/or return an Auditing System singleton instance.
25
+ *
26
+ * @class Auditing
27
+ * @singleton
28
+ * @public
29
+ */
30
+ class Auditing {
31
+
32
+ static #instance = null;
33
+
34
+ /**
35
+ * @constructor
36
+ * @returns {Auditing}
37
+ */
38
+ constructor() {
39
+ if ( !Auditing.#instance ) {
40
+ Auditing.#instance = this;
41
+ }
42
+ return Auditing.#instance;
43
+ }
44
+
45
+ /* Public interface */
46
+
47
+ /**
48
+ * Used to generate a log entry and dispatch it to all enabled logging destinations.
49
+ *
50
+ * @method
51
+ * @param {string} message The primary log message.
52
+ * @param {TiLogSeverity} [severity=DEFAULT] The log severity level. If the current log filtering setting is higher than this then the log entry will be ignored.
53
+ * @param {string} [thread='main'] The logging thread to which the log entry belongs.
54
+ * @param {Object} [data={}] Optional JSON data containing details of the log entry.
55
+ * @public
56
+ */
57
+ log( message, severity = logger.logSeverity.DEFAULT, thread = "main", data = {} ) {
58
+ try {
59
+ // log entries bellow the min allowed level will be ignored:
60
+ if ( severity >= config.getSetting( config.setting.AUDITING_LOG_MIN_LEVEL ) ) {
61
+ // obscure any passwords that might have landed in the data object;
62
+ // also make sure to convert a potential Error object to a JSON:
63
+ let copyOfData = ( config.getSetting( config.setting.AUDITING_LOG_DETAILS ) === true ) ? _.cloneDeep( data ) : undefined;
64
+ let logEntry = Auditing.#createLogEntry( severity, thread, message, copyOfData );
65
+
66
+ // make sure there is a console available:
67
+ if ( config.getSetting( config.setting.AUDITING_LOG_CONSOLE_ENABLED ) === true && console ) {
68
+ Auditing.#logToConsole( logEntry );
69
+ }
70
+
71
+ // if this is an actual error, then send it to GCloud error reporting system as well:
72
+ if ( logEntry.severity >= logger.logSeverity.WARNING && data instanceof Error && gcloud.isEnabled() ) {
73
+ gcloud.reportError( data );
74
+ }
75
+ }
76
+ } catch ( error ) {
77
+ // do nothing here for now...
78
+ }
79
+ }
80
+
81
+ /* Private interface */
82
+
83
+ /**
84
+ * Used to generate a new Log Entry object.
85
+ *
86
+ * @method
87
+ * @param {TiLogSeverity} severity
88
+ * @param {string} thread
89
+ * @param {string} message
90
+ * @param {Object} data
91
+ * @returns {LogEntry}
92
+ * @private
93
+ */
94
+ static #createLogEntry( severity, thread, message, data ) {
95
+ let currentDate = new Date();
96
+ let logDate = tools.getUTCDateString( currentDate );
97
+ let logTime = tools.getUTCTimeString( currentDate, true );
98
+ let reporter = process.env.TI_INSTANCE_ID;
99
+
100
+ return {
101
+ _id: `${ logDate }-${ logTime }-${ thread }-${ reporter }-${ logger.getSeverityName( severity ) }-${ tools.getUUID() }`,
102
+ severity: severity,
103
+ thread: thread,
104
+ reporter: reporter,
105
+ message: message,
106
+ timestamp: currentDate.getTime(),
107
+ data: data
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Used to write the log entries to the system console (i.e. STD OUT and STD ERR).
113
+ * <br/>
114
+ * NOTE: There was an issue in previous Node versions with console that can crash the application if the number of
115
+ * outputs exceeds several thousands per second. To be monitored and adjusted as necessary!
116
+ *
117
+ * @method
118
+ * @param {LogEntry} logEntry
119
+ * @private
120
+ */
121
+ static #logToConsole( logEntry ) {
122
+ if ( config.getSetting( config.setting.AUDITING_LOG_USES_JSON ) === true ) {
123
+ if ( logEntry.severity >= logger.logSeverity.WARNING ) {
124
+ console.error( tools.stringifyJSON( logEntry ) );
125
+ } else {
126
+ console.log( tools.stringifyJSON( logEntry ) );
127
+ }
128
+ } else {
129
+ console.log( Auditing.#formatConsoleMessage( logEntry ) );
130
+ if ( !_.isEmpty( logEntry.data ) ) {
131
+ console.log( ` » ${ Auditing.#formatConsoleData( logEntry ) }` );
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Used to format a log entry for the Node console.
138
+ *
139
+ * @method
140
+ * @param {LogEntry} logEntry
141
+ * @returns {string}
142
+ * @private
143
+ */
144
+ static #formatConsoleMessage( logEntry ) {
145
+ let logDate = new Date( logEntry.timestamp );
146
+ return `${ tools.getUTCDateString( logDate ) }, ${ tools.getUTCTimeString( logDate, true ) } (UTC): ${ logEntry.reporter } - ${ logger.getSeverityName( logEntry.severity ) } - ${ logEntry.message }`;
147
+ }
148
+
149
+ /**
150
+ * Used to format a log entry data payload for the Node console.
151
+ *
152
+ * @method
153
+ * @param {LogEntry} logEntry
154
+ * @returns {string}
155
+ * @private
156
+ */
157
+ static #formatConsoleData( logEntry ) {
158
+ return tools.stringifyJSON( logEntry.data );
159
+ }
160
+ }
161
+
162
+ const instance = new Auditing();
163
+ module.exports = Object.freeze( instance );
@@ -0,0 +1,53 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const exceptions = require( "#exceptions" );
7
+
8
+ /**
9
+ * An abstract class that allows the child class to observe and take action on various events related to external connections.
10
+ *
11
+ * @class ConnectionObserver
12
+ * @abstract
13
+ * @public
14
+ */
15
+ class ConnectionObserver {
16
+
17
+ /**
18
+ * @constructor
19
+ */
20
+ constructor() {
21
+ // make sure this abstract class cannot be instantiated:
22
+ if ( new.target === ConnectionObserver ) {
23
+ throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Needs to be invoked by the connection handler when the connection is disrupted.
29
+ * <br/>
30
+ * NOTE: Override this to add custom functionality.
31
+ *
32
+ * @method
33
+ * @param {string} identifier The identifier of the observed connection.
34
+ * @virtual
35
+ * @public
36
+ */
37
+ onConnectionDisrupted( identifier ) { }
38
+
39
+ /**
40
+ * Needs to be invoked by the connection handler when the connection is recovered.
41
+ * <br/>
42
+ * NOTE: Override this to add custom functionality.
43
+ *
44
+ * @method
45
+ * @param {string} identifier The identifier of the observed connection.
46
+ * @virtual
47
+ * @public
48
+ */
49
+ onConnectionRecovered( identifier ) { }
50
+
51
+ }
52
+
53
+ module.exports = ConnectionObserver;
@@ -0,0 +1,133 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const MessageExchange = require( "#message-exchange" );
7
+ const config = require( "#config" );
8
+ const exceptions = require( "#exceptions" );
9
+
10
+ /**
11
+ * The default {@link MessageExchange} behavior for the Ti Engine using Redis for message exchange.
12
+ *
13
+ * @class DefaultMessageExchange
14
+ * @extends MessageExchange
15
+ * @public
16
+ */
17
+ class DefaultMessageExchange extends MessageExchange {
18
+
19
+ /**
20
+ * @constructor
21
+ * @param {string} instanceID The unique identifier of the microservice instance using the message exchange.
22
+ * @param {string} serviceDomainName The domain name of the microservice using the message exchange.
23
+ */
24
+ constructor( instanceID, serviceDomainName ) {
25
+ super( instanceID, serviceDomainName );
26
+ }
27
+
28
+ /* Public interface */
29
+
30
+ /**
31
+ * Used to initialize the message exchange.
32
+ * <br/>
33
+ * NOTE: This will create and prepare all necessary message handlers and then enable them simultaneously.
34
+ *
35
+ * @method
36
+ * @param {boolean} configureInbound If set to 'true' it tells the message exchange to setup inbound messaging.
37
+ * @param {boolean} configureOutbound If set to 'true' it tells the message exchange to setup outbound messaging.
38
+ * @returns {Promise}
39
+ * @override
40
+ * @public
41
+ */
42
+ enableMessaging( configureInbound, configureOutbound ) {
43
+ return new Promise( ( resolve, reject ) => {
44
+ const DefaultMessageSender = require( "#default-message-sender" );
45
+ const DefaultMessageReceiver = require( "#default-message-receiver" );
46
+
47
+ let handlersToEnable = [];
48
+ if ( configureInbound ) {
49
+ let messageResponsesOut = new DefaultMessageSender( MessageExchange.connectionNameResponsesOut );
50
+ let receiveRequestsQueue = config.getSetting( config.setting.MESSAGE_EXCHANGE_QUEUE_PREFIX ) + MessageExchange.pendingQueue + this.serviceDomainName;
51
+ let messageRequestsIn = new DefaultMessageReceiver( MessageExchange.connectionNameRequestsIn, receiveRequestsQueue );
52
+ messageRequestsIn.addMessageObserver( this );
53
+ this.configureInboundMessaging( messageRequestsIn, messageResponsesOut );
54
+ handlersToEnable.push( this.messageResponsesOut.enable() );
55
+ handlersToEnable.push( this.messageRequestsIn.enable() );
56
+ }
57
+ if ( configureOutbound ) {
58
+ let messageRequestsOut = new DefaultMessageSender( MessageExchange.connectionNameRequestsOut );
59
+ let receiveResponsesQueue = config.getSetting( config.setting.MESSAGE_EXCHANGE_QUEUE_PREFIX ) + MessageExchange.processedQueue + this.serviceDomainName + ":" + this.instanceID;
60
+ let messageResponsesIn = new DefaultMessageReceiver( MessageExchange.connectionNameResponsesIn, receiveResponsesQueue );
61
+ messageResponsesIn.addMessageObserver( this );
62
+ this.configureOutboundMessaging( messageRequestsOut, messageResponsesIn );
63
+ handlersToEnable.push( this.messageRequestsOut.enable() );
64
+ handlersToEnable.push( this.messageResponsesIn.enable() );
65
+ }
66
+
67
+ Promise.all( handlersToEnable ).then( () => {
68
+ resolve();
69
+ } ).catch( ( error ) => {
70
+ reject( exceptions.raise( error ) );
71
+ } );
72
+ } );
73
+ }
74
+
75
+ /**
76
+ * Used to gracefully shut down the message exchange.
77
+ *
78
+ * @method
79
+ * @returns {Promise}
80
+ * @override
81
+ * @public
82
+ */
83
+ disableMessaging() {
84
+ return new Promise( ( resolve, reject ) => {
85
+ let handlersToDisable = [];
86
+ if ( this.configuredInbound ) {
87
+ handlersToDisable.push( this.messageResponsesOut.disable() );
88
+ handlersToDisable.push( this.messageRequestsIn.disable() );
89
+ }
90
+ if ( this.configuredOutbound ) {
91
+ handlersToDisable.push( this.messageRequestsOut.disable() );
92
+ handlersToDisable.push( this.messageResponsesIn.disable() );
93
+ }
94
+
95
+ Promise.all( handlersToDisable ).then( () => {
96
+ resolve();
97
+ } ).catch( ( error ) => {
98
+ reject( exceptions.raise( error ) );
99
+ } );
100
+ } );
101
+ }
102
+
103
+ /**
104
+ * Used to send a message request vie the specified route.
105
+ *
106
+ * @method
107
+ * @param {Message} message The message request to send.
108
+ * @returns {Promise}
109
+ * @override
110
+ * @public
111
+ */
112
+ sendMessageRequest( message ) {
113
+ let sendQueue = config.getSetting( config.setting.MESSAGE_EXCHANGE_QUEUE_PREFIX ) + MessageExchange.pendingQueue + message.destination.route;
114
+ return this.messageRequestsOut.send( message, sendQueue );
115
+ }
116
+
117
+ /**
118
+ * Used to send a message response via the specified route.
119
+ *
120
+ * @method
121
+ * @param {Message} message The message response to send.
122
+ * @returns {Promise}
123
+ * @override
124
+ * @public
125
+ */
126
+ sendMessageResponse( message ) {
127
+ let sendQueue = config.getSetting( config.setting.MESSAGE_EXCHANGE_QUEUE_PREFIX ) + MessageExchange.processedQueue + message.source.route + ":" + message.source.instanceID;
128
+ return this.messageResponsesOut.send( message, sendQueue );
129
+ }
130
+
131
+ }
132
+
133
+ module.exports = DefaultMessageExchange;
@@ -0,0 +1,89 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const MessageReceiver = require( "#message-receiver" );
7
+ const memoryCache = require( "#message-memory-cache" );
8
+ const config = require( "#config" );
9
+ const exceptions = require( "#exceptions" );
10
+
11
+ /**
12
+ * The default {@link MessageReceiver} behavior for the Ti Engine using Redis for message exchange.
13
+ *
14
+ * @class DefaultMessageReceiver
15
+ * @extends MessageReceiver
16
+ * @public
17
+ */
18
+ class DefaultMessageReceiver extends MessageReceiver {
19
+
20
+ #memoryCache;
21
+
22
+ /**
23
+ * @constructor
24
+ * @param {string} identifier An identifier for this message handler. Should be unique in the context of the message exchange.
25
+ * @param {string} receiveQueue The queue from which the messages will be received.
26
+ * @param {string} [processingQueue=undefined] The queue in which the messages will be put for processing (if necessary).
27
+ */
28
+ constructor( identifier, receiveQueue, processingQueue = undefined ) {
29
+ super( identifier, receiveQueue, processingQueue );
30
+ }
31
+
32
+ /**
33
+ * Used to initialize and enable the communication capabilities of the handler.
34
+ *
35
+ * @method
36
+ * @returns {Promise}
37
+ * @override
38
+ * @public
39
+ */
40
+ enable() {
41
+ return new Promise( ( resolve, reject ) => {
42
+ this.#memoryCache = memoryCache.create( this.connectionIdentifier );
43
+ this.#memoryCache.addConnectionObserver( this );
44
+ this.receive();
45
+ resolve();
46
+ } );
47
+ }
48
+
49
+ /**
50
+ * Used to shutdown and disable the communication behavior of the handler.
51
+ *
52
+ * @method
53
+ * @returns {Promise}
54
+ * @override
55
+ * @public
56
+ */
57
+ disable() {
58
+ return new Promise( ( resolve, reject ) => {
59
+ this.isAvailable = false;
60
+ this.#memoryCache = null;
61
+ resolve();
62
+ } );
63
+ }
64
+
65
+ /**
66
+ * Used to receive messages.
67
+ * <br/>
68
+ * NOTE: The default message exchange works with lightweight messages (i.e. will keep the payloads stored in Redis while exchanging).
69
+ *
70
+ * @method
71
+ * @returns {Promise<Message>}
72
+ * @override
73
+ * @public
74
+ */
75
+ onReceive() {
76
+ return new Promise( ( resolve, reject ) => {
77
+ this.#memoryCache.receiveMessage( this.receiveQueue ).then( ( lightweightMessage ) => {
78
+ return this.#memoryCache.retrieveMessagePayload( lightweightMessage, config.getSetting( config.setting.MESSAGE_EXCHANGE_STORE ) );
79
+ } ).then( ( message ) => {
80
+ resolve( message );
81
+ } ).catch( ( error ) => {
82
+ reject( exceptions.raise( error ) );
83
+ } );
84
+ } );
85
+ }
86
+
87
+ }
88
+
89
+ module.exports = DefaultMessageReceiver;
@@ -0,0 +1,90 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const MessageSender = require( "#message-sender" );
7
+ const _ = require( "lodash" );
8
+ const config = require( "#config" );
9
+ const exceptions = require( "#exceptions" );
10
+ const memoryCache = require( "#message-memory-cache" );
11
+
12
+ /**
13
+ * The default {@link MessageSender} behavior for the Ti Engine using Redis for message exchange.
14
+ *
15
+ * @class DefaultMessageSender
16
+ * @extends MessageSender
17
+ * @public
18
+ */
19
+ class DefaultMessageSender extends MessageSender {
20
+
21
+ #memoryCache;
22
+
23
+ /**
24
+ * @constructor
25
+ * @param {string} identifier An identifier for this message handler. Should be unique in the context of the message exchange.
26
+ */
27
+ constructor( identifier ) {
28
+ super( identifier );
29
+ }
30
+
31
+ /**
32
+ * Used to perform the actual sending of a message.
33
+ * <br/>
34
+ * NOTE: The default message exchange works with lightweight messages (i.e. will keep the payloads stored in Redis while exchanging).
35
+ *
36
+ * @method
37
+ * @param {Message} message The message to send.
38
+ * @param {string} queue The route to destination (queue) for the message as recognized by the {@link MessageExchange} implementation.
39
+ * @returns {Promise}
40
+ * @override
41
+ * @public
42
+ */
43
+ onSend( message, queue ) {
44
+ return new Promise( ( resolve, reject ) => {
45
+ this.#memoryCache.storeMessagePayload( message.payload, config.getSetting( config.setting.MESSAGE_EXCHANGE_STORE ) ).then( ( storeID ) => {
46
+ let lightweightMessage = _.cloneDeep( message );
47
+ lightweightMessage.payload = storeID;
48
+ return this.#memoryCache.sendMessage( lightweightMessage, queue );
49
+ } ).then( () => {
50
+ resolve();
51
+ } ).catch( ( error ) => {
52
+ reject( exceptions.raise( error ) );
53
+ } );
54
+ } );
55
+ }
56
+
57
+ /**
58
+ * Used to initialize and enable the communication capabilities of the handler.
59
+ *
60
+ * @method
61
+ * @returns {Promise}
62
+ * @override
63
+ * @public
64
+ */
65
+ enable() {
66
+ return new Promise( ( resolve, reject ) => {
67
+ this.#memoryCache = memoryCache.create( this.connectionIdentifier );
68
+ this.#memoryCache.addConnectionObserver( this );
69
+ resolve();
70
+ } );
71
+ }
72
+
73
+ /**
74
+ * Used to shutdown and disable the communication behavior of the handler.
75
+ *
76
+ * @method
77
+ * @returns {Promise}
78
+ * @override
79
+ * @public
80
+ */
81
+ disable() {
82
+ return new Promise( ( resolve, reject ) => {
83
+ this.isAvailable = false;
84
+ this.#memoryCache = null;
85
+ resolve();
86
+ } );
87
+ }
88
+ }
89
+
90
+ module.exports = DefaultMessageSender;
@@ -0,0 +1,162 @@
1
+ /*
2
+ * SPDX-FileCopyrightText: © 2021 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * SPDX-License-Identifier: ICU
4
+ */
5
+
6
+ const tools = require( "#tools" );
7
+ const exceptions = require( "#exceptions" );
8
+ const logger = require( "#logger" );
9
+ const messageTracer = require( "#message-tracer" );
10
+
11
+ /**
12
+ * Used to create and/or return a Message Dispatcher singleton instance.
13
+ * This class handles the internal message dispatching between the microservices.
14
+ *
15
+ * @class MessageDispatcher
16
+ * @singleton
17
+ * @public
18
+ */
19
+ class MessageDispatcher {
20
+
21
+ static #instance = null;
22
+ #messageExchange;
23
+
24
+ /**
25
+ * @constructor
26
+ * @returns {MessageDispatcher}
27
+ */
28
+ constructor() {
29
+ if ( !MessageDispatcher.#instance ) {
30
+ MessageDispatcher.#instance = this;
31
+ }
32
+
33
+ return MessageDispatcher.#instance;
34
+ }
35
+
36
+ /* Public interface */
37
+
38
+ /**
39
+ * Used to initialize the message dispatcher and enable the message exchange.
40
+ *
41
+ * @method
42
+ * @param {MessageExchange} messageExchange The message exchange instance to be used by the dispatcher.
43
+ * @param {boolean} configureInbound If set to 'true' it tells the message exchange to setup inbound messaging.
44
+ * @param {boolean} configureOutbound If set to 'true' it tells the message exchange to setup outbound messaging.
45
+ * @returns {Promise}
46
+ * @public
47
+ */
48
+ initialize( messageExchange, configureInbound, configureOutbound ) {
49
+ return new Promise( ( resolve, reject ) => {
50
+ this.#messageExchange = messageExchange;
51
+ this.#messageExchange.enableMessaging( configureInbound, configureOutbound ).then( () => {
52
+ resolve();
53
+ } ).catch( ( error ) => {
54
+ reject( exceptions.raise( error ) );
55
+ } );
56
+ } );
57
+ }
58
+
59
+ /**
60
+ * Used to shut down the message dispatcher and disable the message exchange.
61
+ *
62
+ * @method
63
+ * @returns {Promise}
64
+ * @public
65
+ */
66
+ shutDown() {
67
+ return new Promise( ( resolve, reject ) => {
68
+ this.#messageExchange.disableMessaging().then( () => {
69
+ this.#messageExchange = null;
70
+ resolve();
71
+ } ).catch( ( error ) => {
72
+ reject( exceptions.raise( error ) );
73
+ } );
74
+ } );
75
+ }
76
+
77
+ /**
78
+ * Used to send a message request via the message exchange system.
79
+ *
80
+ * @method
81
+ * @param {Message} message The message to send. This can also be a subclass of {@link Message}.
82
+ * @returns {Promise<string>}
83
+ * @public
84
+ */
85
+ sendRequest( message ) {
86
+ return new Promise( ( resolve, reject ) => {
87
+ let retry = new tools.RetryPolicy( 3 );
88
+ retry.onFailedAttempt( ( error ) => {
89
+ logger.log( `Failed to send message request with chain ID: ${ message.chainID }`, logger.logSeverity.WARNING, error );
90
+ } );
91
+ retry.onRetry( ( attempt ) => {
92
+ logger.log( `Retrying to send message response with chain ID: ${ message.chainID }. This is attempt ${ attempt }...`, logger.logSeverity.NOTICE );
93
+ } );
94
+
95
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_REQUEST, messageTracer.dispatchEvent.SENT, messageTracer.messageState.PENDING );
96
+
97
+ retry.execute( this.#messageExchange, this.#messageExchange.sendMessageRequest, [ message ] ).then( () => {
98
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_REQUEST, messageTracer.dispatchEvent.DELIVERED, messageTracer.messageState.PENDING );
99
+ resolve( message.messageID );
100
+ } ).catch( ( error ) => {
101
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_REQUEST, messageTracer.dispatchEvent.FAILED, messageTracer.messageState.PENDING );
102
+ reject( exceptions.raise( error ) );
103
+ } );
104
+ } );
105
+ }
106
+
107
+ /**
108
+ * Used to send a message response via the message exchange system.
109
+ *
110
+ * @method
111
+ * @param {Message} message The message to send. This can also be a subclass of {@link Message}.
112
+ * @returns {Promise}
113
+ * @public
114
+ */
115
+ sendResponse( message ) {
116
+ return new Promise( ( resolve, reject ) => {
117
+ let retry = new tools.RetryPolicy( 3 );
118
+ retry.onFailedAttempt( ( error ) => {
119
+ logger.log( `Failed to send message response with chain ID: ${ message.chainID }`, logger.logSeverity.WARNING, error );
120
+ } );
121
+ retry.onRetry( ( attempt ) => {
122
+ logger.log( `Retrying to send message response with chain ID: ${ message.chainID }. This is attempt ${ attempt }...`, logger.logSeverity.NOTICE );
123
+ } );
124
+
125
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_RESPONSE, messageTracer.dispatchEvent.SENT, messageTracer.messageState.PROCESSED );
126
+
127
+ retry.execute( this.#messageExchange, this.#messageExchange.sendMessageResponse, [ message ] ).then( () => {
128
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_RESPONSE, messageTracer.dispatchEvent.DELIVERED, messageTracer.messageState.PROCESSED );
129
+ resolve();
130
+ } ).catch( ( error ) => {
131
+ messageTracer.recordTraceEntry( message, messageTracer.messageType.MESSAGE_RESPONSE, messageTracer.dispatchEvent.FAILED, messageTracer.messageState.PROCESSED );
132
+ reject( exceptions.raise( error ) );
133
+ } );
134
+ } );
135
+ }
136
+
137
+ /**
138
+ * Used to add an additional {@link MessageObserver} to the connection for the incoming message requests.
139
+ *
140
+ * @method
141
+ * @param {MessageObserver} messageObserver
142
+ * @public
143
+ */
144
+ addMessageObserverRequestsIn( messageObserver ) {
145
+ this.#messageExchange.addMessageObserverRequestsIn( messageObserver );
146
+ }
147
+
148
+ /**
149
+ * Used to add an additional {@link MessageObserver} to the connection for the incoming message responses.
150
+ *
151
+ * @method
152
+ * @param {MessageObserver} messageObserver
153
+ * @public
154
+ */
155
+ addMessageObserverResponsesIn( messageObserver ) {
156
+ this.#messageExchange.addMessageObserverResponsesIn( messageObserver );
157
+ }
158
+
159
+ }
160
+
161
+ const instance = new MessageDispatcher();
162
+ module.exports = Object.freeze( instance );