@mojaloop/sdk-scheme-adapter 24.2.0-csi-1210.6 → 24.2.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.
Binary file
package/CHANGELOG.md CHANGED
@@ -1,4 +1,18 @@
1
1
  # Changelog: [mojaloop/sdk-scheme-adapter](https://github.com/mojaloop/sdk-scheme-adapter)
2
+ ## [24.2.0](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.1.3...v24.2.0) (2025-03-05)
3
+
4
+
5
+ ### Features
6
+
7
+ * add code to restart sdk on broken control client connection ([#555](https://github.com/mojaloop/sdk-scheme-adapter/issues/555)) ([4ce10e8](https://github.com/mojaloop/sdk-scheme-adapter/commit/4ce10e8c1385471e1cf33fc0abe923210167a099))
8
+ * **csi-1210:** added OTel header to Outbound/Inbound TransfersModel ([#552](https://github.com/mojaloop/sdk-scheme-adapter/issues/552)) ([cbbd0b7](https://github.com/mojaloop/sdk-scheme-adapter/commit/cbbd0b7e0e26eb85d04178a1e8cdb2120b9f4f6f))
9
+
10
+
11
+ ### Chore
12
+
13
+ * add better service checks to docker compose ([#556](https://github.com/mojaloop/sdk-scheme-adapter/issues/556)) ([ba0d7f0](https://github.com/mojaloop/sdk-scheme-adapter/commit/ba0d7f04ba7a138363c85edad5ba0bef0b7e5302))
14
+ * add heartbeat to test server ([#557](https://github.com/mojaloop/sdk-scheme-adapter/issues/557)) ([5d7b628](https://github.com/mojaloop/sdk-scheme-adapter/commit/5d7b6284e93c081359089f6865c726f05d2f44b9))
15
+
2
16
  ### [24.1.3](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.1.2...v24.1.3) (2025-02-25)
3
17
 
4
18
 
@@ -19,9 +19,12 @@ services:
19
19
  - "4002:4002"
20
20
  - "9229:9229"
21
21
  depends_on:
22
- - redis
23
- - ml-testing-toolkit
24
- - kafka
22
+ redis:
23
+ condition: service_healthy
24
+ ml-testing-toolkit:
25
+ condition: service_started
26
+ kafka:
27
+ condition: service_healthy
25
28
  command: yarn nx run modules-api-svc:start
26
29
  volumes:
27
30
  - ./docker/wait4:/tmp/wait4
@@ -144,7 +147,8 @@ services:
144
147
  environment:
145
148
  - KAFKA_BROKERS=kafka:29092
146
149
  depends_on:
147
- - kafka
150
+ kafka:
151
+ condition: service_healthy
148
152
  profiles:
149
153
  - debug
150
154
 
@@ -153,7 +157,8 @@ services:
153
157
  - mojaloop-net
154
158
  image: docker.io/bitnami/kafka:3.4.0
155
159
  depends_on:
156
- - kafka
160
+ kafka:
161
+ condition: service_healthy
157
162
  entrypoint: [ '/bin/sh', '-c' ]
158
163
  command: |
159
164
  "
@@ -73,7 +73,7 @@
73
73
  "@mojaloop/logging-bc-client-lib": "0.5.8",
74
74
  "@mojaloop/ml-schema-transformer-lib": "2.5.6",
75
75
  "@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
76
- "@mojaloop/sdk-standard-components": "v19.9.0",
76
+ "@mojaloop/sdk-standard-components": "19.9.0",
77
77
  "ajv": "8.17.1",
78
78
  "axios": "1.8.1",
79
79
  "body-parser": "1.20.3",
@@ -86,7 +86,8 @@ module.exports = {
86
86
  __parseResourceVersion: parseResourceVersions,
87
87
  control: {
88
88
  mgmtAPIWsUrl: env.get('MGMT_API_WS_URL').default('127.0.0.1').asString(),
89
- mgmtAPIWsPort: env.get('MGMT_API_WS_PORT').default('4005').asPortNumber()
89
+ mgmtAPIWsPort: env.get('MGMT_API_WS_PORT').default('4005').asPortNumber(),
90
+ mgmtAPILatencyAssumption: env.get('MGMT_API_LATENCY_ASSUMPTION').default('2000').asIntPositive(),
90
91
  },
91
92
  idGenerator: env.get('ID_GENERATOR').default('{"type":"ulid"}').asJsonObject(),
92
93
  logLevel: env.get('LOG_LEVEL').default('info').asEnum(Logger.logLevels),
@@ -62,6 +62,8 @@ const LOG_ID = {
62
62
  CACHE: { component: 'cache' },
63
63
  };
64
64
 
65
+ const PING_INTERVAL_MS = 30000;
66
+
65
67
  const createCache = (config, logger) => new Cache({
66
68
  cacheUrl: config.cacheUrl,
67
69
  logger: logger.push(LOG_ID.CACHE),
@@ -78,6 +80,7 @@ class Server extends EventEmitter {
78
80
  this.conf = conf;
79
81
  this.logger = logger;
80
82
  this.cache = createCache(conf, logger);
83
+ this.pingTimeout;
81
84
 
82
85
  this.metricsClient = new MetricsClient();
83
86
 
@@ -166,12 +169,33 @@ class Server extends EventEmitter {
166
169
  appConfig: this.conf,
167
170
  });
168
171
  this.controlClient.on(ControlAgent.EVENT.RECONFIGURE, this.restart.bind(this));
169
- this.controlClient.on('close', () => setTimeout(() => {
170
- this.logger.push({ currentConf: this.conf }).debug('Control client closed. Restarting server...');
171
- this.restart(_.merge({}, this.conf, {
172
- control: { stopped: Date.now() }
173
- }));
174
- }, RESTART_INTERVAL_MS));
172
+
173
+ const schedulePing = () => {
174
+ clearTimeout(this.pingTimeout);
175
+ this.pingTimeout = setTimeout(() => {
176
+ this.logger.error('Ping timeout, possible broken connection. Restarting server...');
177
+ this.restart(_.merge({}, this.conf, {
178
+ control: { stopped: Date.now() }
179
+ }));
180
+ }, PING_INTERVAL_MS + this.conf.control.mgmtAPILatencyAssumption);
181
+ };
182
+
183
+ this.controlClient.on('ping', () => {
184
+ this.logger.debug('Received ping from control server');
185
+ schedulePing();
186
+ });
187
+
188
+ this.controlClient.on('close', () => {
189
+ clearTimeout(this.pingTimeout);
190
+ setTimeout(() => {
191
+ this.logger.debug('Control client closed. Restarting server...');
192
+ this.restart(_.merge({}, this.conf, {
193
+ control: { stopped: Date.now() }
194
+ }));
195
+ }, RESTART_INTERVAL_MS);
196
+ });
197
+
198
+ schedulePing();
175
199
  }
176
200
 
177
201
  await Promise.all([
@@ -284,12 +308,33 @@ class Server extends EventEmitter {
284
308
  appConfig: newConf,
285
309
  });
286
310
  this.controlClient.on(ControlAgent.EVENT.RECONFIGURE, this.restart.bind(this));
287
- this.controlClient.on('close', () => setTimeout(() => {
288
- this.logger.push({ newConf }).debug('Control client closed. Restarting server...');
289
- this.restart(_.merge({}, newConf, {
290
- control: { stopped: Date.now() }
291
- }));
292
- }, RESTART_INTERVAL_MS));
311
+
312
+ const schedulePing = () => {
313
+ clearTimeout(this.pingTimeout);
314
+ this.pingTimeout = setTimeout(() => {
315
+ this.logger.error('Ping timeout, possible broken connection. Restarting server...');
316
+ this.restart(_.merge({}, newConf, {
317
+ control: { stopped: Date.now() }
318
+ }));
319
+ }, PING_INTERVAL_MS + this.conf.control.mgmtAPILatencyAssumption);
320
+ };
321
+
322
+ this.controlClient.on('ping', () => {
323
+ this.logger.debug('Received ping from control server');
324
+ schedulePing();
325
+ });
326
+
327
+ this.controlClient.on('close', () => {
328
+ clearTimeout(this.pingTimeout);
329
+ setTimeout(() => {
330
+ this.logger.debug('Control client closed. Restarting server...');
331
+ this.restart(_.merge({}, newConf, {
332
+ control: { stopped: Date.now() }
333
+ }));
334
+ }, RESTART_INTERVAL_MS);
335
+ });
336
+
337
+ schedulePing();
293
338
  restartActionsTaken.updateControlClient = true;
294
339
  }
295
340
  }
@@ -337,6 +382,7 @@ class Server extends EventEmitter {
337
382
  }
338
383
 
339
384
  stop() {
385
+ clearTimeout(this.pingTimeout);
340
386
  this.wso2.auth.stop();
341
387
  this.controlClient?.removeAllListeners();
342
388
  this.inboundServer.removeAllListeners();
@@ -113,6 +113,7 @@ class Server extends ws.Server {
113
113
  _clientData;
114
114
  _currentConfig;
115
115
  _isConfigUpdated;
116
+ _heartbeatInterval;
116
117
 
117
118
  constructor(opts) {
118
119
  super({ clientTracking: true, port: opts.port });
@@ -134,7 +135,14 @@ class Server extends ws.Server {
134
135
  remoteAddress: req.socket.remoteAddress,
135
136
  });
136
137
  logger.log('Websocket connection received');
137
- this._clientData.set(socket, { ip: req.connection.remoteAddress, logger });
138
+ this._clientData.set(socket, { ip: req.connection.remoteAddress, logger, isAlive: true });
139
+
140
+ socket.on('pong', () => {
141
+ const clientData = this._clientData.get(socket);
142
+ if (clientData) {
143
+ clientData.isAlive = true;
144
+ }
145
+ });
138
146
 
139
147
  socket.on('close', (code, reason) => {
140
148
  logger.push({ code, reason }).log('Websocket connection closed');
@@ -144,6 +152,31 @@ class Server extends ws.Server {
144
152
  socket.on('message', this._handle(socket, logger));
145
153
  });
146
154
  this._logger.push(this.address()).log('running on');
155
+ this._startHeartbeat();
156
+ }
157
+
158
+ _startHeartbeat() {
159
+ this._heartbeatInterval = setInterval(() => {
160
+ this.clients.forEach((client) => {
161
+ const clientData = this._clientData.get(client);
162
+ if (clientData && !clientData.isAlive) {
163
+ client.terminate();
164
+ this._clientData.delete(client);
165
+ return;
166
+ }
167
+ if (clientData) {
168
+ clientData.isAlive = false;
169
+ }
170
+ client.ping();
171
+ });
172
+ }, 30000);
173
+ }
174
+
175
+ _stopHeartbeat() {
176
+ if (this._heartbeatInterval) {
177
+ clearInterval(this._heartbeatInterval);
178
+ this._heartbeatInterval = null;
179
+ }
147
180
  }
148
181
 
149
182
  _setConfig(newConfig) {
@@ -158,6 +191,7 @@ class Server extends ws.Server {
158
191
 
159
192
  // Close the server then wait for all the client sockets to close
160
193
  async stop() {
194
+ this._stopHeartbeat();
161
195
  const closing = new Promise((resolve) => this.close(resolve));
162
196
  for (const client of this.clients) {
163
197
  client.terminate();
@@ -73,6 +73,7 @@ describe('Server', () => {
73
73
  conf.control.mgmtAPIWsUrl = 'localhost';
74
74
  conf.control.mgmtAPIWsPort = 4005;
75
75
  conf.control.port = conf.control.mgmtAPIWsPort;
76
+ conf.control.mgmtAPILatencyAssumption = 2000;
76
77
  controlServer = new TestControlServer.Server({ logger, appConfig: conf });
77
78
  server = new index.Server(conf, logger);
78
79
  server.restart = jest.fn();
@@ -221,13 +221,6 @@ describe('OutboundTransfersModel Tests', () => {
221
221
  expect(model.data.traceId.length).toBe(32);
222
222
  });
223
223
 
224
- // test('should generate traceId based on transferId value', async () => {
225
- // const model = await createAndInitModel();
226
- // expect(model.data.transferId.length).toBe(26);
227
- // expect(model.data.traceId.length).toBe(32);
228
- // // todo: check traceID value
229
- // });
230
-
231
224
  test('executes all three transfer stages without halting when AUTO_ACCEPT_PARTY and AUTO_ACCEPT_QUOTES are true', async () => {
232
225
  config.autoAcceptParty = true;
233
226
  config.autoAcceptQuotes = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter",
3
- "version": "24.2.0-csi-1210.6",
3
+ "version": "24.2.0",
4
4
  "description": "mojaloop sdk-scheme-adapter",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/sdk-scheme-adapter",