grexxconnect-ess 1.1.1 → 1.2.1

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/client.js CHANGED
@@ -91,6 +91,10 @@ class WebSocketClient extends EventEmitter {
91
91
  }
92
92
  }
93
93
 
94
+ disconnect(code, message) {
95
+ this.instance.close(code, message);
96
+ }
97
+
94
98
  reconnect( code, msg ) {
95
99
  this.log('connection failed'.red, `retry in ${this.autoReconnectInterval}ms`, code.toString().red, (msg || '').yellow);
96
100
  this.instance.removeAllListeners();
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (C) 2014-2021 Grexx - All Rights Reserved
2
+ * Copyright (C) 2014-2022 Grexx - All Rights Reserved
3
3
  * Unauthorized copying of this file, via any medium is strictly prohibited
4
4
  * Proprietary and confidential
5
5
  */
@@ -8,8 +8,10 @@
8
8
  const EventEmitter = require('events');
9
9
  const WebSocket = require('./client');
10
10
 
11
+ const TIMEOUT_LONG = 30 * 1000; //== 30 seconds
12
+
11
13
  class EssConnect extends EventEmitter {
12
- constructor( settings ) {
14
+ constructor(settings) {
13
15
  super();
14
16
 
15
17
  const thisVersion = require('./package.json').version;
@@ -21,51 +23,128 @@ class EssConnect extends EventEmitter {
21
23
  debug: settings.debug
22
24
  });
23
25
 
24
- const send = ( type, content ) => {
25
- return new Promise(( resolve, reject ) => {
26
+ const _waitQueue = {},
27
+ _unfinishedResponses = {};
28
+
29
+ const send = (type, content, timeout = TIMEOUT_LONG) => {
30
+ return new Promise((resolve, reject) => {
31
+ const essCorrelationId = content?.['ess-correlation-id'];
32
+
33
+ if (!essCorrelationId || !this._ackFunctional) {
34
+ try {
35
+ ws.send(JSON.stringify({
36
+ type: type,
37
+ content: content,
38
+ timestamp: Date.now()
39
+ }));
40
+ return resolve();
41
+ } catch (error) {
42
+ return reject(error);
43
+ }
44
+ }
45
+
46
+ const exceededTimeout = setTimeout(async () => {
47
+ ws.debug(`timeout of ${timeout}ms exceeded for ack response from server`);
48
+ return reject('Timeout exceeded');
49
+ }, timeout);
50
+
51
+ _waitQueue[essCorrelationId] = () => {
52
+ clearTimeout(exceededTimeout);
53
+ ws.debug(`ack response received from server`);
54
+ return resolve('Ack response received from server');
55
+ };
56
+
26
57
  ws.send(JSON.stringify({
27
58
  type: type,
28
59
  content: content,
29
60
  timestamp: Date.now()
30
- }), error => {
31
- if ( error ) {
32
- reject(error);
33
- } else {
34
- resolve();
61
+ }), (error) => {
62
+ if (error) {
63
+ return reject(error);
35
64
  }
36
65
  });
37
66
  });
38
67
  };
39
68
 
40
- ws.on('close', ( code, msg ) => this.emit('disconnected', code, msg));
41
- ws.on('message', _message => {
42
- switch ( _message.type ) {
69
+ const sendAck = async (connectCorrelationId, message = '', type = 'ack') => {
70
+ this.ws.debug(`acknowledging message from server for message header ID: ${connectCorrelationId}`.gray);
71
+ await send(type, {'connect-correlation-id': connectCorrelationId, message});
72
+ this.emit(type);
73
+ };
74
+
75
+ const completeUnfinishedResponses = () => {
76
+ this.ws.debug(`checking for any incomplete message responses: ${Object.keys(_unfinishedResponses).length.toString().blue} unfinished responses`);
77
+ for (const key in _unfinishedResponses) {
78
+ const {type, content} = _unfinishedResponses[key];
79
+ send(type, content).then(() => {
80
+ delete _unfinishedResponses[key];
81
+ }).catch(() => handleTimeout(type, content));
82
+ }
83
+ };
84
+
85
+ const handleTimeout = (type, content) => {
86
+ const id = content['ess-correlation-id'];
87
+ _unfinishedResponses[id] = {type, content};
88
+ ws.disconnect(1012, "Ack response from server timed out");
89
+ };
90
+
91
+ ws.on('close', (code, msg) => this.emit('disconnected', code, msg));
92
+ ws.on('message', async (_message) => {
93
+ const connectCorrelationId = _message['connect-correlation-id'],
94
+ essCorrelationId = _message['ess-correlation-id'];
95
+
96
+ switch (_message.type) {
43
97
  case 'ping':
44
- this.ws.debug('responding to connection-ping');
45
- send('pong');
98
+ this.ws.debug('responding to connection-ping'.gray);
46
99
  this.emit('ping');
100
+ await send('pong');
101
+ break;
102
+ case 'ackFunctional':
103
+ this.ws.debug('responding to functionality check'.green, 'acknowledgements'.blue);
104
+ this._ackFunctional = true;
105
+ await sendAck(connectCorrelationId, null, 'ackFunctional');
106
+ completeUnfinishedResponses();
107
+ break;
108
+ case 'ack':
109
+ this.ws.debug(`received ack from GrexxConnect for message with header ID: ${essCorrelationId}`);
110
+ if (typeof (essCorrelationId) != 'undefined' && typeof (_waitQueue[essCorrelationId]) == 'function') {
111
+ const execFunc = _waitQueue[essCorrelationId];
112
+ execFunc();
113
+ delete _waitQueue[essCorrelationId];
114
+ }
47
115
  break;
48
116
  case 'authorized':
49
117
  this.log('connected and authorized for'.green, _message.namespace.blue);
50
118
  this.emit('authorized', _message.namespace);
51
119
  break;
120
+ case 'opened':
121
+ this.log('open'.green, 'ready to receive messages'.blue);
122
+ break;
52
123
  case 'system':
53
124
  this.handleSystemMessage(_message, send);
54
125
  break;
55
126
  case'message':
127
+ await sendAck(connectCorrelationId, "message received and being processed");
56
128
  this.emit('message', {
57
129
  content: _message.content,
58
130
  parsed: this.parseMessage(_message.content.data),
59
131
  resolve: outputData => {
60
- return send('response', {
61
- toMessage: _message,
62
- outputData: outputData
132
+ const type = 'response',
133
+ content = {
134
+ 'ess-correlation-id': connectCorrelationId,
135
+ toMessage: _message,
136
+ outputData: outputData
137
+ };
138
+
139
+ return send(type, content).catch(() => {
140
+ handleTimeout(type, content);
63
141
  });
64
142
  },
65
- reject: ( publicMessage, debugMessage, additionalContext ) => {
143
+ reject: (publicMessage, debugMessage, additionalContext) => {
66
144
  return send('reject', {
67
- toMessage: _message,
68
- errorContext: Object.assign({
145
+ 'ess-correlation-id': connectCorrelationId,
146
+ toMessage: _message,
147
+ errorContext: Object.assign({
69
148
  publicMessage,
70
149
  debugMessage
71
150
  }, additionalContext)
@@ -96,27 +175,27 @@ class EssConnect extends EventEmitter {
96
175
  /**Set a monitoring function. Should return {status:'ok|warning|error', ...}
97
176
  * @param {function} statusFunction
98
177
  */
99
- setMonitoringResponse( statusFunction ) {
178
+ setMonitoringResponse(statusFunction) {
100
179
  this._statusFunction = statusFunction;
101
180
  }
102
181
 
103
- log( ...args ) {
182
+ log(...args) {
104
183
  console.info(' [ESS][MAIN] '.gray, ...args);
105
184
  }
106
185
 
107
- handleSystemMessage( message, send ) {
108
- if ( message.content.action === 'ping' ) {
109
- this.ws.debug('responding to system-ping');
186
+ handleSystemMessage(message, send) {
187
+ if (message.content.action === 'ping') {
188
+ this.ws.debug('responding to system-ping'.gray);
110
189
  send('system', {
111
190
  action: 'pong',
112
191
  client_data: typeof this._statusFunction === 'function' ?
113
- this._statusFunction() : { status: 'ok' },
192
+ this._statusFunction() : {status: 'ok'},
114
193
  server_data: message.content.id
115
194
  });
116
195
  }
117
196
  }
118
197
 
119
- parseMessage( message = [] ) {
198
+ parseMessage(message = []) {
120
199
  const result = {};
121
200
 
122
201
  message.forEach(part => {
package/package.json CHANGED
@@ -2,7 +2,8 @@
2
2
  "name" : "grexxconnect-ess",
3
3
  "author" : "Grexx <info@grexxboxx.com>",
4
4
  "description" : "Grexx Connect - External System Service Helper",
5
- "version" : "1.1.1",
5
+ "version" : "1.2.1",
6
+ "types" : "./schema/index.d.ts",
6
7
  "contributors" : [
7
8
  {
8
9
  "name" : "Jeffrey van Norden",
@@ -19,6 +20,6 @@
19
20
  },
20
21
  "license" : "MIT",
21
22
  "engines" : {
22
- "node": ">=6.9.1"
23
+ "node": ">=12"
23
24
  }
24
25
  }
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright (C) 2014-2022 Grexx - All Rights Reserved
3
+ * Unauthorized copying of this file, via any medium is strictly prohibited
4
+ * Proprietary and confidential
5
+ */
6
+
7
+ export = WebSocketClient;
8
+ declare class WebSocketClient extends EventEmitter {
9
+ constructor(url: any, options: any);
10
+ number: number;
11
+ autoReconnectInterval: number;
12
+ _debug: any;
13
+ log(...args: any[]): void;
14
+ debug(...args: any[]): void;
15
+ open(url: any, options: any): void;
16
+ url: any;
17
+ options: any;
18
+ instance: any;
19
+ send(data: any, option: any, cb: any): void;
20
+ reconnect(code: any, msg: any): void;
21
+ onOpen(...args: any[]): void;
22
+ onMessage(data: any, flags: any, number: any): void;
23
+ onError(error: any): void;
24
+ onClose(code: any, msg: any): void;
25
+ }
26
+ import EventEmitter = require("node/events");
@@ -0,0 +1,21 @@
1
+ /*
2
+ * Copyright (C) 2014-2022 Grexx - All Rights Reserved
3
+ * Unauthorized copying of this file, via any medium is strictly prohibited
4
+ * Proprietary and confidential
5
+ */
6
+
7
+ export = EssConnect;
8
+ declare class EssConnect extends EventEmitter {
9
+ constructor(settings: any);
10
+ ws: WebSocket;
11
+ /**Set a monitoring function. Should return {status:'ok|warning|error', ...}
12
+ * @param {function} statusFunction
13
+ */
14
+ setMonitoringResponse(statusFunction: Function): void;
15
+ _statusFunction: Function | undefined;
16
+ log(...args: any[]): void;
17
+ handleSystemMessage(message: any, send: any): void;
18
+ parseMessage(message?: any[]): {};
19
+ }
20
+ import EventEmitter = require("node/events");
21
+ import WebSocket = require("./client");