@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.
- package/components/auditing.js +163 -0
- package/components/connection-observer.js +53 -0
- package/components/exchange/default/default-message-exchange.js +133 -0
- package/components/exchange/default/default-message-receiver.js +89 -0
- package/components/exchange/default/default-message-sender.js +90 -0
- package/components/exchange/message-dispatcher.js +162 -0
- package/components/exchange/message-exchange.js +418 -0
- package/components/exchange/message-handler.js +173 -0
- package/components/exchange/message-memory-cache.js +150 -0
- package/components/exchange/message-observer.js +76 -0
- package/components/exchange/message-receiver.js +113 -0
- package/components/exchange/message-sender.js +133 -0
- package/components/exchange/message-tracer.js +146 -0
- package/components/service-caller.js +306 -0
- package/components/service-consumer.js +112 -0
- package/components/service-executor.js +231 -0
- package/components/service-instance.js +280 -0
- package/components/service-provider.js +221 -0
- package/integrations/gcloud-integration.js +61 -0
- package/integrations/redis-integration.js +261 -0
- package/package.json +54 -0
- package/settings.json +33 -0
- package/utils/cache.js +507 -0
- package/utils/config.js +146 -0
- package/utils/exceptions.js +241 -0
- package/utils/logger.js +69 -0
- package/utils/tools.js +537 -0
|
@@ -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 );
|