@mojaloop/sdk-scheme-adapter 24.15.0 → 24.15.2

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.
Files changed (35) hide show
  1. package/.grype.yaml +1 -0
  2. package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.34.1-fc3be8e73c-f7ad2f9394.zip → @mojaloop-central-services-shared-npm-18.34.2-df841bbba3-3daa9af31e.zip} +0 -0
  3. package/.yarn/cache/{@mojaloop-event-sdk-npm-14.8.0-c3e8d5a581-bc2be19e16.zip → @mojaloop-event-sdk-npm-14.8.1-f2a5de549c-49fe01b89c.zip} +0 -0
  4. package/.yarn/cache/@types-node-npm-24.8.1-bc0371b5f2-4f94446676.zip +0 -0
  5. package/.yarn/cache/{@typescript-eslint-eslint-plugin-npm-8.46.0-e6114965b4-415afd894a.zip → @typescript-eslint-eslint-plugin-npm-8.46.1-a9079cb527-9fd8c27958.zip} +0 -0
  6. package/.yarn/cache/{@typescript-eslint-parser-npm-8.46.0-c44629050a-6838fde776.zip → @typescript-eslint-parser-npm-8.46.1-cfdf46e1e9-4edcb49bb0.zip} +0 -0
  7. package/.yarn/cache/{@typescript-eslint-project-service-npm-8.46.0-85a4b9bb9c-de11af23ae.zip → @typescript-eslint-project-service-npm-8.46.1-ebb88f07ff-d63cbb8852.zip} +0 -0
  8. package/.yarn/cache/{@typescript-eslint-scope-manager-npm-8.46.0-fd8edaba78-ed85abd08c.zip → @typescript-eslint-scope-manager-npm-8.46.1-ad4c0a55e0-3d73812087.zip} +0 -0
  9. package/.yarn/cache/{@typescript-eslint-tsconfig-utils-npm-8.46.0-8919c1f746-e78a66a854.zip → @typescript-eslint-tsconfig-utils-npm-8.46.1-4c9ab3591b-f033d68a53.zip} +0 -0
  10. package/.yarn/cache/{@typescript-eslint-type-utils-npm-8.46.0-dbfff922bb-5405b71b91.zip → @typescript-eslint-type-utils-npm-8.46.1-2441cbea81-db989c1f55.zip} +0 -0
  11. package/.yarn/cache/@typescript-eslint-types-npm-8.46.1-79fefa883d-d162ddf6d7.zip +0 -0
  12. package/.yarn/cache/{@typescript-eslint-typescript-estree-npm-8.46.0-0b10d4388a-61053bd0c3.zip → @typescript-eslint-typescript-estree-npm-8.46.1-7899272fc5-af068a14d6.zip} +0 -0
  13. package/.yarn/cache/{@typescript-eslint-utils-npm-8.46.0-a7d3832f43-4e0da60de3.zip → @typescript-eslint-utils-npm-8.46.1-c04d3c3a0c-a8fed8aebd.zip} +0 -0
  14. package/.yarn/cache/{@typescript-eslint-visitor-keys-npm-8.46.0-7d793afea5-37e6145b6a.zip → @typescript-eslint-visitor-keys-npm-8.46.1-6179cc42f8-eed1c5ce08.zip} +0 -0
  15. package/.yarn/cache/openapi-typescript-npm-7.10.1-0695e3203a-531627b682.zip +0 -0
  16. package/.yarn/cache/semver-npm-7.7.3-9cf7b3b46c-8dbc3168e0.zip +0 -0
  17. package/.yarn/cache/{ts-jest-npm-29.4.4-fd3c97fbf0-759913fdb9.zip → ts-jest-npm-29.4.5-5dad11fc5b-48d867e070.zip} +0 -0
  18. package/.yarn/cache/winston-npm-3.18.3-60bcb643a0-0d94690e05.zip +0 -0
  19. package/.yarn/install-state.gz +0 -0
  20. package/CHANGELOG.md +16 -0
  21. package/CLAUDE.md +2 -0
  22. package/modules/api-svc/package.json +3 -3
  23. package/modules/api-svc/src/SdkServer.js +560 -0
  24. package/modules/api-svc/src/index.js +8 -529
  25. package/modules/api-svc/src/lib/model/InboundTransfersModel.js +15 -0
  26. package/modules/api-svc/src/lib/utils.js +20 -1
  27. package/modules/api-svc/test/unit/{index.configPolling.test.js → SdkServer.configPolling.test.js} +11 -11
  28. package/modules/api-svc/test/unit/lib/model/InboundTransfersModel.test.js +215 -0
  29. package/modules/outbound-command-event-handler/package.json +5 -5
  30. package/modules/outbound-domain-event-handler/package.json +4 -4
  31. package/modules/private-shared-lib/package.json +5 -5
  32. package/package.json +5 -5
  33. package/{sbom-v24.14.0.csv → sbom-v24.15.1.csv} +23 -23
  34. package/.yarn/cache/@types-node-npm-24.7.0-fa253cad8d-db0b77e9b1.zip +0 -0
  35. package/.yarn/cache/@typescript-eslint-types-npm-8.46.0-b013400d3e-0118b0dd59.zip +0 -0
@@ -35,8 +35,8 @@ jest.mock('~/lib/cache');
35
35
  jest.mock('~/ControlAgent');
36
36
 
37
37
  const promClient = require('prom-client');
38
+ const SdkServer = require('../../src/SdkServer');
38
39
  const ControlAgent = require('~/ControlAgent');
39
- const { Server } = require('~/index');
40
40
  const { logger } = require('~/lib/logger');
41
41
  const testConfig = require('./data/defaultConfig.json');
42
42
 
@@ -92,7 +92,7 @@ describe('Config Polling Tests -->', () => {
92
92
  }
93
93
  };
94
94
 
95
- server = new Server(config, logger);
95
+ server = new SdkServer(config, logger);
96
96
  await server.start();
97
97
 
98
98
  await jest.advanceTimersByTimeAsync(POLL_INTERVAL_MS * 2);
@@ -105,7 +105,7 @@ describe('Config Polling Tests -->', () => {
105
105
  pm4mlEnabled: false,
106
106
  };
107
107
 
108
- server = new Server(config, logger);
108
+ server = new SdkServer(config, logger);
109
109
  await server.start();
110
110
 
111
111
  await jest.advanceTimersByTimeAsync(POLL_INTERVAL_MS * 2);
@@ -113,7 +113,7 @@ describe('Config Polling Tests -->', () => {
113
113
  });
114
114
 
115
115
  it('should poll at configured interval', async () => {
116
- server = new Server(mockConfig, logger);
116
+ server = new SdkServer(mockConfig, logger);
117
117
  await server.start();
118
118
  expect(mockControlAgent.getUpdatedConfig).toHaveBeenCalledTimes(0);
119
119
 
@@ -130,7 +130,7 @@ describe('Config Polling Tests -->', () => {
130
130
  jwsSigningKey: newJwsKey
131
131
  });
132
132
 
133
- server = new Server(mockConfig, logger);
133
+ server = new SdkServer(mockConfig, logger);
134
134
  const restartSpy = jest.spyOn(server, 'restart');
135
135
 
136
136
  await server.start();
@@ -150,7 +150,7 @@ describe('Config Polling Tests -->', () => {
150
150
  it('should handle unchanged config efficiently (no-op)', async () => {
151
151
  mockControlAgent.getUpdatedConfig.mockResolvedValue({});
152
152
 
153
- server = new Server(mockConfig, logger);
153
+ server = new SdkServer(mockConfig, logger);
154
154
  const restartSpy = jest.spyOn(server, 'restart');
155
155
  await server.start();
156
156
 
@@ -165,7 +165,7 @@ describe('Config Polling Tests -->', () => {
165
165
  it('should prevent race condition when update already in progress', async () => {
166
166
  mockControlAgent.getUpdatedConfig.mockResolvedValue({ jwsSigningKey: 'new-key' });
167
167
 
168
- server = new Server(mockConfig, logger);
168
+ server = new SdkServer(mockConfig, logger);
169
169
  await server.start();
170
170
  // Simulate restart in progress
171
171
  server._configUpdateInProgress = true;
@@ -176,7 +176,7 @@ describe('Config Polling Tests -->', () => {
176
176
  });
177
177
 
178
178
  it('should skip polling when WebSocket not OPEN', async () => {
179
- server = new Server(mockConfig, logger);
179
+ server = new SdkServer(mockConfig, logger);
180
180
  await server.start();
181
181
 
182
182
  mockControlAgent.getUpdatedConfig.mockClear();
@@ -190,7 +190,7 @@ describe('Config Polling Tests -->', () => {
190
190
  it('should handle network errors gracefully during polling', async () => {
191
191
  mockControlAgent.getUpdatedConfig.mockRejectedValue(new Error('Connection refused'));
192
192
 
193
- server = new Server(mockConfig, logger);
193
+ server = new SdkServer(mockConfig, logger);
194
194
  await server.start();
195
195
  await jest.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
196
196
 
@@ -204,7 +204,7 @@ describe('Config Polling Tests -->', () => {
204
204
  it('should handle missing config response', async () => {
205
205
  mockControlAgent.getUpdatedConfig.mockResolvedValue(null);
206
206
 
207
- server = new Server(mockConfig, logger);
207
+ server = new SdkServer(mockConfig, logger);
208
208
  await server.start();
209
209
  await jest.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
210
210
 
@@ -214,7 +214,7 @@ describe('Config Polling Tests -->', () => {
214
214
  });
215
215
 
216
216
  it('should stop polling when server stops', async () => {
217
- server = new Server(mockConfig, logger);
217
+ server = new SdkServer(mockConfig, logger);
218
218
  await server.start();
219
219
 
220
220
  expect(server._configPollInterval).toBeDefined();
@@ -41,6 +41,7 @@ jest.mock('~/lib/model/lib/requests',() => require('./mockedLibRequests'));
41
41
 
42
42
  const randomUUID = require('@mojaloop/central-services-shared').Util.id({type: 'ulid'});
43
43
  const { MojaloopRequests, Ilp } = require('@mojaloop/sdk-standard-components');
44
+ const axios = require('axios');
44
45
  const { logger } = require('~/lib/logger');
45
46
  const { BackendRequests, HTTPResponseError } = require('~/lib/model/lib/requests');
46
47
  const Cache = require('~/lib/cache');
@@ -1426,4 +1427,218 @@ describe('inboundModel', () => {
1426
1427
  });
1427
1428
  // todo: add error case tests
1428
1429
  });
1430
+ describe('sendNotificationToPayee: retry logic for 4xx errors', () => {
1431
+ const transferId = '1234';
1432
+ let cache;
1433
+
1434
+ beforeEach(async () => {
1435
+ cache = new Cache({
1436
+ cacheUrl: 'redis://dummy:1234',
1437
+ logger,
1438
+ unsubscribeTimeoutMs: 5000
1439
+ });
1440
+ await cache.connect();
1441
+ });
1442
+
1443
+ afterEach(async () => {
1444
+ await cache.disconnect();
1445
+ });
1446
+
1447
+ test('does not retry notification to fsp backend on 4xx error', async () => {
1448
+ const error = new axios.AxiosError(
1449
+ 'Bad Request',
1450
+ 'ERR_BAD_REQUEST',
1451
+ {
1452
+ method: 'put',
1453
+ url: '/transfersNotification',
1454
+ headers: {},
1455
+ data: {},
1456
+ },
1457
+ {},
1458
+ {
1459
+ status: 400,
1460
+ statusText: 'Bad Request',
1461
+ headers: {},
1462
+ config: {},
1463
+ data: {
1464
+ statusCode: '400',
1465
+ message: 'Bad Request'
1466
+ }
1467
+ }
1468
+ );
1469
+ const mockFn = jest.fn().mockRejectedValue(error);
1470
+ BackendRequests.__putTransfersNotification = mockFn;
1471
+
1472
+ const notif = JSON.parse(JSON.stringify(notificationToPayee));
1473
+ const model = new Model({
1474
+ ...config,
1475
+ cache,
1476
+ logger,
1477
+ backendRequestRetry: {
1478
+ enabled: true,
1479
+ maxRetries: 3,
1480
+ retryDelayMs: 10,
1481
+ maxRetryDelayMs: 20,
1482
+ backoffFactor: 1
1483
+ }
1484
+ });
1485
+
1486
+ await model.sendNotificationToPayee(notif.data, transferId);
1487
+ // Should only be called once, no retry
1488
+ expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(1);
1489
+ });
1490
+
1491
+ test('does not retry notification to fsp backend on 404 error', async () => {
1492
+
1493
+ const error = new axios.AxiosError(
1494
+ 'Not Found',
1495
+ 'ERR_BAD_REQUEST',
1496
+ {
1497
+ method: 'put',
1498
+ url: '/transfersNotification',
1499
+ headers: {},
1500
+ data: {},
1501
+ },
1502
+ {},
1503
+ {
1504
+ status: 404,
1505
+ statusText: 'Not Found',
1506
+ headers: {},
1507
+ config: {},
1508
+ data: {
1509
+ statusCode: '404',
1510
+ message: 'Not Found'
1511
+ }
1512
+ }
1513
+ );
1514
+ const mockFn = jest.fn().mockRejectedValue(error);
1515
+ BackendRequests.__putTransfersNotification = mockFn;
1516
+
1517
+ const notif = JSON.parse(JSON.stringify(notificationToPayee));
1518
+ const model = new Model({
1519
+ ...config,
1520
+ cache,
1521
+ logger,
1522
+ backendRequestRetry: {
1523
+ enabled: true,
1524
+ maxRetries: 3,
1525
+ retryDelayMs: 10,
1526
+ maxRetryDelayMs: 20,
1527
+ backoffFactor: 1
1528
+ }
1529
+ });
1530
+
1531
+ await model.sendNotificationToPayee(notif.data, transferId);
1532
+ // Should only be called once, no retry
1533
+ expect(BackendRequests.__putTransfersNotification).toHaveBeenCalledTimes(1);
1534
+ });
1535
+ });
1536
+
1537
+ describe('sendFxPutNotificationToBackend: retry logic for 4xx errors', () => {
1538
+ const conversionId = '1234';
1539
+ let cache;
1540
+
1541
+ beforeEach(async () => {
1542
+ cache = new Cache({
1543
+ cacheUrl: 'redis://dummy:1234',
1544
+ logger,
1545
+ unsubscribeTimeoutMs: 5000
1546
+ });
1547
+ await cache.connect();
1548
+ });
1549
+
1550
+ afterEach(async () => {
1551
+ await cache.disconnect();
1552
+ });
1553
+
1554
+ test('does not retry notification to backend on 4xx error', async () => {
1555
+ const error = new axios.AxiosError(
1556
+ 'Bad Request',
1557
+ 'ERR_BAD_REQUEST',
1558
+ {
1559
+ method: 'put',
1560
+ url: '/fxTransfersNotification',
1561
+ headers: {},
1562
+ data: {},
1563
+ },
1564
+ {},
1565
+ {
1566
+ status: 400,
1567
+ statusText: 'Bad Request',
1568
+ headers: {},
1569
+ config: {},
1570
+ data: {
1571
+ statusCode: '400',
1572
+ message: 'Bad Request'
1573
+ }
1574
+ }
1575
+ );
1576
+ const mockFn = jest.fn().mockRejectedValue(error);
1577
+ BackendRequests.__putFxTransfersNotification = mockFn;
1578
+
1579
+ const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
1580
+ const model = new Model({
1581
+ ...config,
1582
+ cache,
1583
+ logger,
1584
+ backendRequestRetry: {
1585
+ enabled: true,
1586
+ maxRetries: 3,
1587
+ retryDelayMs: 10,
1588
+ maxRetryDelayMs: 20,
1589
+ backoffFactor: 1
1590
+ }
1591
+ });
1592
+ model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
1593
+
1594
+ await model.sendFxPutNotificationToBackend(notif.data, conversionId);
1595
+ // Should only be called once, no retry
1596
+ expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(1);
1597
+ });
1598
+
1599
+ test('does not retry notification to backend on 404 error', async () => {
1600
+ const error = new axios.AxiosError(
1601
+ 'Not Found',
1602
+ 'ERR_BAD_REQUEST',
1603
+ {
1604
+ method: 'put',
1605
+ url: '/fxTransfersNotification',
1606
+ headers: {},
1607
+ data: {},
1608
+ },
1609
+ {},
1610
+ {
1611
+ status: 404,
1612
+ statusText: 'Not Found',
1613
+ headers: {},
1614
+ config: {},
1615
+ data: {
1616
+ statusCode: '404',
1617
+ message: 'Not Found'
1618
+ }
1619
+ }
1620
+ );
1621
+ const mockFn = jest.fn().mockRejectedValue(error);
1622
+ BackendRequests.__putFxTransfersNotification = mockFn;
1623
+
1624
+ const notif = JSON.parse(JSON.stringify(fxNotificationToBackend));
1625
+ const model = new Model({
1626
+ ...config,
1627
+ cache,
1628
+ logger,
1629
+ backendRequestRetry: {
1630
+ enabled: true,
1631
+ maxRetries: 3,
1632
+ retryDelayMs: 10,
1633
+ maxRetryDelayMs: 20,
1634
+ backoffFactor: 1
1635
+ }
1636
+ });
1637
+ model.saveFxState = jest.fn().mockReturnValue(Promise.resolve({}));
1638
+
1639
+ await model.sendFxPutNotificationToBackend(notif.data, conversionId);
1640
+ // Should only be called once, no retry
1641
+ expect(BackendRequests.__putFxTransfersNotification).toHaveBeenCalledTimes(1);
1642
+ });
1643
+ });
1429
1644
  });
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@mojaloop/api-snippets": "18.2.0",
45
- "@mojaloop/central-services-shared": "18.34.1",
45
+ "@mojaloop/central-services-shared": "18.34.2",
46
46
  "@mojaloop/logging-bc-client-lib": "0.5.8",
47
47
  "@mojaloop/logging-bc-public-types-lib": "0.5.6",
48
48
  "@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
@@ -60,13 +60,13 @@
60
60
  "@types/convict": "6.1.6",
61
61
  "@types/express": "5.0.3",
62
62
  "@types/jest": "30.0.0",
63
- "@types/node": "24.7.0",
63
+ "@types/node": "24.8.1",
64
64
  "@types/node-cache": "4.2.5",
65
65
  "@types/supertest": "6.0.3",
66
66
  "@types/swagger-ui-express": "4.1.8",
67
67
  "@types/yamljs": "0.2.34",
68
- "@typescript-eslint/eslint-plugin": "8.46.0",
69
- "@typescript-eslint/parser": "8.46.0",
68
+ "@typescript-eslint/eslint-plugin": "8.46.1",
69
+ "@typescript-eslint/parser": "8.46.1",
70
70
  "copyfiles": "2.4.1",
71
71
  "eslint": "9.15.0",
72
72
  "jest": "29.7.0",
@@ -74,7 +74,7 @@
74
74
  "npm-check-updates": "16.7.10",
75
75
  "replace": "1.2.2",
76
76
  "standard-version": "9.5.0",
77
- "ts-jest": "29.4.4",
77
+ "ts-jest": "29.4.5",
78
78
  "ts-node": "10.9.2",
79
79
  "typescript": "5.9.3"
80
80
  },
@@ -57,13 +57,13 @@
57
57
  "@types/convict": "6.1.6",
58
58
  "@types/express": "5.0.3",
59
59
  "@types/jest": "30.0.0",
60
- "@types/node": "24.7.0",
60
+ "@types/node": "24.8.1",
61
61
  "@types/node-cache": "4.2.5",
62
62
  "@types/supertest": "6.0.3",
63
63
  "@types/swagger-ui-express": "4.1.8",
64
64
  "@types/yamljs": "0.2.34",
65
- "@typescript-eslint/eslint-plugin": "8.46.0",
66
- "@typescript-eslint/parser": "8.46.0",
65
+ "@typescript-eslint/eslint-plugin": "8.46.1",
66
+ "@typescript-eslint/parser": "8.46.1",
67
67
  "copyfiles": "2.4.1",
68
68
  "eslint": "9.15.0",
69
69
  "jest": "29.7.0",
@@ -71,7 +71,7 @@
71
71
  "npm-check-updates": "16.7.10",
72
72
  "replace": "1.2.2",
73
73
  "standard-version": "9.5.0",
74
- "ts-jest": "29.4.4",
74
+ "ts-jest": "29.4.5",
75
75
  "ts-node": "10.9.2",
76
76
  "typescript": "5.9.3"
77
77
  },
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@mojaloop/api-snippets": "18.2.0",
33
- "@mojaloop/central-services-shared": "18.34.1",
33
+ "@mojaloop/central-services-shared": "18.34.2",
34
34
  "@mojaloop/logging-bc-public-types-lib": "0.5.6",
35
35
  "@mojaloop/platform-shared-lib-messaging-types-lib": "0.7.3",
36
36
  "@mojaloop/platform-shared-lib-nodejs-kafka-client-lib": "0.5.18",
@@ -40,16 +40,16 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@eslint/compat": "1.4.0",
43
- "@types/node": "24.7.0",
43
+ "@types/node": "24.8.1",
44
44
  "@types/uuid": "10.0.0",
45
- "@typescript-eslint/eslint-plugin": "8.46.0",
46
- "@typescript-eslint/parser": "8.46.0",
45
+ "@typescript-eslint/eslint-plugin": "8.46.1",
46
+ "@typescript-eslint/parser": "8.46.1",
47
47
  "eslint": "9.15.0",
48
48
  "jest": "29.7.0",
49
49
  "npm-check-updates": "16.7.10",
50
50
  "replace": "1.2.2",
51
51
  "standard-version": "9.5.0",
52
- "ts-jest": "29.4.4",
52
+ "ts-jest": "29.4.5",
53
53
  "tslib": "2.8.1",
54
54
  "typescript": "5.9.3"
55
55
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojaloop/sdk-scheme-adapter",
3
- "version": "24.15.0",
3
+ "version": "24.15.2",
4
4
  "description": "mojaloop sdk-scheme-adapter",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/mojaloop/sdk-scheme-adapter",
@@ -83,10 +83,10 @@
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/jest": "30.0.0",
86
- "@types/node": "24.7.0",
86
+ "@types/node": "24.8.1",
87
87
  "@types/node-cache": "4.2.5",
88
- "@typescript-eslint/eslint-plugin": "8.46.0",
89
- "@typescript-eslint/parser": "8.46.0",
88
+ "@typescript-eslint/eslint-plugin": "8.46.1",
89
+ "@typescript-eslint/parser": "8.46.1",
90
90
  "audit-ci": "7.1.0",
91
91
  "eslint": "9.15.0",
92
92
  "eslint-config-airbnb-typescript": "18.0.0",
@@ -98,7 +98,7 @@
98
98
  "npm-check-updates": "16.7.10",
99
99
  "replace": "1.2.2",
100
100
  "standard-version": "9.5.0",
101
- "ts-jest": "29.4.4",
101
+ "ts-jest": "29.4.5",
102
102
  "ts-node": "10.9.2",
103
103
  "typescript": "5.9.3",
104
104
  "yarn-audit-fix": "10.1.1"