@mojaloop/sdk-scheme-adapter 24.15.1 → 24.15.3

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 (88) hide show
  1. package/.grype.yaml +1 -0
  2. package/.yarn/cache/@babel-compat-data-npm-7.28.5-41f8d327e8-5a5ff00b18.zip +0 -0
  3. package/.yarn/cache/@babel-core-npm-7.28.5-cd68c2d8db-2f1e224125.zip +0 -0
  4. package/.yarn/cache/@babel-generator-npm-7.28.5-fd8f3ae6b1-ae618f0a17.zip +0 -0
  5. package/.yarn/cache/@babel-helper-validator-identifier-npm-7.28.5-1953d49d2b-8e5d9b0133.zip +0 -0
  6. package/.yarn/cache/@babel-parser-npm-7.28.5-f2345a6b62-8d9bfb437a.zip +0 -0
  7. package/.yarn/cache/@babel-plugin-bugfix-firefox-class-in-computed-class-key-npm-7.28.5-086662e626-750de98b34.zip +0 -0
  8. package/.yarn/cache/{@babel-plugin-transform-block-scoping-npm-7.28.4-f325d4f6d4-0848c681b0.zip → @babel-plugin-transform-block-scoping-npm-7.28.5-83f7baa513-4b695360ed.zip} +0 -0
  9. package/.yarn/cache/@babel-plugin-transform-destructuring-npm-7.28.5-5a0083928d-9cc67d3377.zip +0 -0
  10. package/.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.28.5-b1db8006da-da9bb5acd3.zip +0 -0
  11. package/.yarn/cache/@babel-plugin-transform-logical-assignment-operators-npm-7.28.5-6bdd0633bb-c76778f4b1.zip +0 -0
  12. package/.yarn/cache/{@babel-plugin-transform-modules-systemjs-npm-7.27.1-8b05b5a514-06d7bf76ac.zip → @babel-plugin-transform-modules-systemjs-npm-7.28.5-1fe3e218f1-1b91b48488.zip} +0 -0
  13. package/.yarn/cache/@babel-plugin-transform-optional-chaining-npm-7.28.5-18f86d8ec3-0bc900bff6.zip +0 -0
  14. package/.yarn/cache/{@babel-preset-env-npm-7.28.3-ec87d1a73a-b09991276a.zip → @babel-preset-env-npm-7.28.5-6424c6f247-e9a5136a7e.zip} +0 -0
  15. package/.yarn/cache/@babel-traverse-npm-7.28.5-2b51d83636-1fce426f5e.zip +0 -0
  16. package/.yarn/cache/@babel-types-npm-7.28.5-582d7cca8a-4256bb9fb2.zip +0 -0
  17. package/.yarn/cache/{@eslint-compat-npm-1.4.0-2352faedcd-204f80bfde.zip → @eslint-compat-npm-1.4.1-68e5276ed9-2345ba0991.zip} +0 -0
  18. package/.yarn/cache/@eslint-core-npm-0.17.0-8579df04c4-f9a428cc65.zip +0 -0
  19. package/.yarn/cache/{@mojaloop-central-services-error-handling-npm-13.1.3-d798c7ad31-4eae5147c7.zip → @mojaloop-central-services-error-handling-npm-13.1.4-d3bef7695b-13ce908b20.zip} +0 -0
  20. package/.yarn/cache/{@mojaloop-central-services-metrics-npm-12.8.0-e245e8b50d-627dc16865.zip → @mojaloop-central-services-metrics-npm-12.8.1-7935c82b41-49044a40a6.zip} +0 -0
  21. package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.34.1-fc3be8e73c-f7ad2f9394.zip → @mojaloop-central-services-shared-npm-18.34.3-923ad0cfc3-980c7f869d.zip} +0 -0
  22. 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
  23. package/.yarn/cache/{@redis-bloom-npm-5.8.3-892faaab15-031bfb7d85.zip → @redis-bloom-npm-5.9.0-cc4091258a-a97626f4e9.zip} +0 -0
  24. package/.yarn/cache/{@redis-client-npm-5.8.3-acc7573cc3-402e5f4520.zip → @redis-client-npm-5.9.0-1bff2f6c15-09c0d9cd7a.zip} +0 -0
  25. package/.yarn/cache/{@redis-json-npm-5.8.3-bbddd9239e-fc81b7e638.zip → @redis-json-npm-5.9.0-cfc3099015-60d820ee4d.zip} +0 -0
  26. package/.yarn/cache/{@redis-search-npm-5.8.3-07f03dcedf-74c4ef9d3f.zip → @redis-search-npm-5.9.0-bc105c5d83-ebf738be42.zip} +0 -0
  27. package/.yarn/cache/{@redis-time-series-npm-5.8.3-a029734baa-20ab29dfe1.zip → @redis-time-series-npm-5.9.0-dfbf77d4a9-683f5b28a8.zip} +0 -0
  28. package/.yarn/cache/@types-express-npm-5.0.5-450717ee89-9e72410286.zip +0 -0
  29. package/.yarn/cache/@types-formidable-npm-3.4.6-e4589e1e18-25ddb37eb1.zip +0 -0
  30. package/.yarn/cache/@types-koa-npm-3.0.1-68d382ee88-b1581d31d5.zip +0 -0
  31. package/.yarn/cache/@types-node-npm-24.10.0-293a9fc97d-bc3abd9adc.zip +0 -0
  32. package/.yarn/cache/@types-send-npm-0.17.6-fe650e1f5c-4948ab32ab.zip +0 -0
  33. package/.yarn/cache/@types-serve-static-npm-1.15.10-7bd7926ff3-d9be724875.zip +0 -0
  34. package/.yarn/cache/{@typescript-eslint-eslint-plugin-npm-8.46.0-e6114965b4-415afd894a.zip → @typescript-eslint-eslint-plugin-npm-8.46.3-11cf690280-0c1eb81a43.zip} +0 -0
  35. package/.yarn/cache/{@typescript-eslint-parser-npm-8.45.0-9132cc3400-4f8b7c73ae.zip → @typescript-eslint-parser-npm-8.46.3-4b6a18a0cc-d36edeba9c.zip} +0 -0
  36. package/.yarn/cache/{@typescript-eslint-project-service-npm-8.46.0-85a4b9bb9c-de11af23ae.zip → @typescript-eslint-project-service-npm-8.46.3-7b731144a3-2f041dfc66.zip} +0 -0
  37. package/.yarn/cache/{@typescript-eslint-scope-manager-npm-8.46.0-fd8edaba78-ed85abd08c.zip → @typescript-eslint-scope-manager-npm-8.46.3-20b51e3735-6bb6c3210b.zip} +0 -0
  38. package/.yarn/cache/{@typescript-eslint-tsconfig-utils-npm-8.46.0-8919c1f746-e78a66a854.zip → @typescript-eslint-tsconfig-utils-npm-8.46.3-aca33a5784-e7a16eadf7.zip} +0 -0
  39. package/.yarn/cache/{@typescript-eslint-type-utils-npm-8.46.0-dbfff922bb-5405b71b91.zip → @typescript-eslint-type-utils-npm-8.46.3-10f94e440e-b29cd001c7.zip} +0 -0
  40. package/.yarn/cache/@typescript-eslint-types-npm-8.46.3-236ab30652-3de35df2ec.zip +0 -0
  41. package/.yarn/cache/{@typescript-eslint-typescript-estree-npm-8.46.0-0b10d4388a-61053bd0c3.zip → @typescript-eslint-typescript-estree-npm-8.46.3-8915f6d2e4-b55cf72fe3.zip} +0 -0
  42. package/.yarn/cache/{@typescript-eslint-utils-npm-8.46.0-a7d3832f43-4e0da60de3.zip → @typescript-eslint-utils-npm-8.46.3-6931f1e5e2-369c962bc2.zip} +0 -0
  43. package/.yarn/cache/{@typescript-eslint-visitor-keys-npm-8.46.0-7d793afea5-37e6145b6a.zip → @typescript-eslint-visitor-keys-npm-8.46.3-a6e2e64cde-02659a4cc4.zip} +0 -0
  44. package/.yarn/cache/axios-npm-1.12.0-58e24ac59e-6c45e294b6.zip +0 -0
  45. package/.yarn/cache/brace-expansion-npm-2.0.2-bc7f134bbc-01dff195e3.zip +0 -0
  46. package/.yarn/cache/immutable-npm-5.1.4-cddca3d05c-0655b33af2.zip +0 -0
  47. package/.yarn/cache/koa-body-npm-7.0.0-535082bb2a-b3c35a0805.zip +0 -0
  48. package/.yarn/cache/{koa-npm-3.0.1-b25d97703b-0e56f77f71.zip → koa-npm-3.0.3-69f0f9bdfc-15df9a7777.zip} +0 -0
  49. package/.yarn/cache/openapi-typescript-npm-7.10.1-0695e3203a-531627b682.zip +0 -0
  50. package/.yarn/cache/redis-npm-5.9.0-dc6753f6da-bf2f1ca0c2.zip +0 -0
  51. package/.yarn/cache/semver-npm-7.7.3-9cf7b3b46c-8dbc3168e0.zip +0 -0
  52. package/.yarn/cache/{tar-npm-7.5.1-7b414f7fec-4848cd2fa2.zip → tar-npm-7.5.2-6d8cfb7a13-dbad9c9a07.zip} +0 -0
  53. package/.yarn/cache/{ts-jest-npm-29.4.4-fd3c97fbf0-759913fdb9.zip → ts-jest-npm-29.4.5-5dad11fc5b-48d867e070.zip} +0 -0
  54. package/.yarn/cache/{undici-types-npm-7.14.0-d9eb643e6a-0f8709b214.zip → undici-types-npm-7.16.0-0e23b08124-db43439f69.zip} +0 -0
  55. package/.yarn/cache/{validator-npm-13.15.15-143146ad19-a43d9271c8.zip → validator-npm-13.15.20-81be70a87b-498f9b201d.zip} +0 -0
  56. package/.yarn/cache/winston-npm-3.18.3-60bcb643a0-0d94690e05.zip +0 -0
  57. package/.yarn/cache/zod-npm-4.1.12-8e1ffc4d68-c5f04e6ac3.zip +0 -0
  58. package/.yarn/install-state.gz +0 -0
  59. package/CHANGELOG.md +20 -0
  60. package/modules/api-svc/jest.config.js +4 -4
  61. package/modules/api-svc/package.json +14 -12
  62. package/modules/api-svc/src/lib/model/InboundTransfersModel.js +15 -0
  63. package/modules/api-svc/src/lib/utils.js +20 -1
  64. package/modules/api-svc/test/unit/lib/model/InboundTransfersModel.test.js +215 -0
  65. package/modules/outbound-command-event-handler/jest.config.js +4 -4
  66. package/modules/outbound-command-event-handler/package.json +10 -10
  67. package/modules/outbound-domain-event-handler/jest.config.js +4 -4
  68. package/modules/outbound-domain-event-handler/package.json +8 -8
  69. package/modules/private-shared-lib/jest.config.js +4 -4
  70. package/modules/private-shared-lib/package.json +10 -8
  71. package/package.json +11 -8
  72. package/{sbom-v24.15.0.csv → sbom-v24.15.2.csv} +78 -77
  73. package/.yarn/cache/@babel-plugin-bugfix-firefox-class-in-computed-class-key-npm-7.27.1-f0c584df24-fe65257d5b.zip +0 -0
  74. package/.yarn/cache/@babel-plugin-transform-exponentiation-operator-npm-7.27.1-ce51b745ac-dbbedd2472.zip +0 -0
  75. package/.yarn/cache/@babel-plugin-transform-logical-assignment-operators-npm-7.27.1-b46ecdb249-2757955d81.zip +0 -0
  76. package/.yarn/cache/@eslint-core-npm-0.16.0-ceefcac859-3cea45971b.zip +0 -0
  77. package/.yarn/cache/@types-formidable-npm-2.0.6-94d53f71b6-808a9bc112.zip +0 -0
  78. package/.yarn/cache/@types-koa-npm-2.15.0-fe318dc6d8-2be9dff1ef.zip +0 -0
  79. package/.yarn/cache/@types-node-npm-24.7.0-fa253cad8d-db0b77e9b1.zip +0 -0
  80. package/.yarn/cache/@typescript-eslint-eslint-plugin-npm-8.45.0-92bb8b7596-6d31dbd335.zip +0 -0
  81. package/.yarn/cache/@typescript-eslint-parser-npm-8.46.0-c44629050a-6838fde776.zip +0 -0
  82. package/.yarn/cache/@typescript-eslint-type-utils-npm-8.45.0-7a1226a6e4-81017b3f47.zip +0 -0
  83. package/.yarn/cache/@typescript-eslint-types-npm-8.46.0-b013400d3e-0118b0dd59.zip +0 -0
  84. package/.yarn/cache/axios-npm-1.8.2-55671cda10-d432875812.zip +0 -0
  85. package/.yarn/cache/brace-expansion-npm-2.0.1-17aa2616f9-a61e7cd2e8.zip +0 -0
  86. package/.yarn/cache/koa-body-npm-6.0.1-e672d44ab2-d241d4d228.zip +0 -0
  87. package/.yarn/cache/redis-npm-5.8.3-3369ceaea3-3843f0ad95.zip +0 -0
  88. package/.yarn/cache/zod-npm-3.25.76-7de26333f8-f0c963ec40.zip +0 -0
package/.grype.yaml CHANGED
@@ -5,6 +5,7 @@ ignore:
5
5
  - vulnerability: CVE-2025-9230
6
6
  - vulnerability: CVE-2025-9231
7
7
  - vulnerability: CVE-2025-9232
8
+ - vulnerability: GHSA-9965-vmph-33xx # validator 13.15.15
8
9
 
9
10
 
10
11
  # Set output format defaults
Binary file
package/CHANGELOG.md CHANGED
@@ -1,4 +1,24 @@
1
1
  # Changelog: [mojaloop/sdk-scheme-adapter](https://github.com/mojaloop/sdk-scheme-adapter)
2
+ ### [24.15.3](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.15.2...v24.15.3) (2025-11-11)
3
+
4
+
5
+ ### Chore
6
+
7
+ * **sbom:** update sbom [skip ci] ([b87dfbd](https://github.com/mojaloop/sdk-scheme-adapter/commit/b87dfbd1098208de521e4dc522bb5105387b2a88))
8
+
9
+
10
+ ### Tests
11
+
12
+ * re-enable code coverage pi29 ([#627](https://github.com/mojaloop/sdk-scheme-adapter/issues/627)) ([5c69d9a](https://github.com/mojaloop/sdk-scheme-adapter/commit/5c69d9a08dc5d2e2bd30a458de4a98dbcef8886f))
13
+
14
+ ### [24.15.2](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.15.1...v24.15.2) (2025-10-17)
15
+
16
+
17
+ ### Chore
18
+
19
+ * adjust retry logic ([#621](https://github.com/mojaloop/sdk-scheme-adapter/issues/621)) ([ce2b72d](https://github.com/mojaloop/sdk-scheme-adapter/commit/ce2b72d33fff72e3b3566390c150fdfbe3b8d24b))
20
+ * **sbom:** update sbom [skip ci] ([e50b0d1](https://github.com/mojaloop/sdk-scheme-adapter/commit/e50b0d1c16a7e84e36691ad2fe3949e555fb2f14))
21
+
2
22
  ### [24.15.1](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.15.0...v24.15.1) (2025-10-08)
3
23
 
4
24
 
@@ -5,10 +5,10 @@ module.exports = {
5
5
  ],
6
6
  coverageThreshold: {
7
7
  global: {
8
- statements: 90,
9
- functions: 90,
10
- branches: 90,
11
- lines: 90
8
+ statements: 74,
9
+ functions: 71,
10
+ branches: 57,
11
+ lines: 74
12
12
  }
13
13
  },
14
14
  clearMocks: true,
@@ -28,6 +28,8 @@
28
28
  "test": "yarn run test:unit",
29
29
  "test:unit": "jest --runInBand --ci --reporters=default --reporters=jest-junit --env=node test/unit/ --setupFilesAfterEnv=./test/unit/setup.js",
30
30
  "test:xunit": "JEST_JUNIT_OUTPUT_DIR=../test/reports/ JEST_JUNIT_OUTPUT_NAME=xunit-api-svc.xml yarn run test:unit -- --reporters=jest-junit",
31
+ "test:coverage": "jest --runInBand --ci --coverage --env=node test/unit/ --setupFilesAfterEnv=./test/unit/setup.js",
32
+ "test:coverage-check": "jest --runInBand --ci --coverage --env=node test/unit/ --setupFilesAfterEnv=./test/unit/setup.js",
31
33
  "test:integration": "jest --ci --reporters=default --reporters=jest-junit --env=node test/integration/",
32
34
  "test:integration-pm4ml": "jest --ci --reporters=default --reporters=jest-junit --env=node test/integration-pm4ml/",
33
35
  "test:pm4ml-run-mock-servers": "node test/integration-pm4ml/mockServers/index.js",
@@ -64,18 +66,18 @@
64
66
  },
65
67
  "dependencies": {
66
68
  "@koa/cors": "5.0.0",
67
- "@mojaloop/api-snippets": "18.2.0",
68
- "@mojaloop/central-services-error-handling": "13.1.3",
69
+ "@mojaloop/api-snippets": "18.2.1",
70
+ "@mojaloop/central-services-error-handling": "13.1.4",
69
71
  "@mojaloop/central-services-logger": "11.10.1",
70
- "@mojaloop/central-services-metrics": "12.8.0",
71
- "@mojaloop/central-services-shared": "18.34.1",
72
- "@mojaloop/event-sdk": "14.8.0",
72
+ "@mojaloop/central-services-metrics": "12.8.1",
73
+ "@mojaloop/central-services-shared": "18.34.3",
74
+ "@mojaloop/event-sdk": "14.8.1",
73
75
  "@mojaloop/logging-bc-client-lib": "0.5.8",
74
76
  "@mojaloop/ml-schema-transformer-lib": "2.7.8",
75
77
  "@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
76
78
  "@mojaloop/sdk-standard-components": "19.18.0",
77
79
  "ajv": "8.17.1",
78
- "axios": "1.12.2",
80
+ "axios": "1.13.2",
79
81
  "body-parser": "2.2.0",
80
82
  "co-body": "6.2.0",
81
83
  "dotenv": "17.2.3",
@@ -87,8 +89,8 @@
87
89
  "js-yaml": "4.1.0",
88
90
  "json-schema-ref-parser": "9.0.9",
89
91
  "knex": "3.1.0",
90
- "koa": "3.0.1",
91
- "koa-body": "6.0.1",
92
+ "koa": "3.1.1",
93
+ "koa-body": "7.0.0",
92
94
  "lodash": "4.17.21",
93
95
  "module-alias": "2.2.3",
94
96
  "oauth2-server": "4.0.0-dev.2",
@@ -96,14 +98,14 @@
96
98
  "prom-client": "15.1.3",
97
99
  "promise-timeout": "1.3.0",
98
100
  "random-word-slugs": "0.1.7",
99
- "redis": "5.8.3",
101
+ "redis": "5.9.0",
100
102
  "retry": "^0.13.1",
101
103
  "uuidv4": "6.2.13",
102
104
  "ws": "8.18.3"
103
105
  },
104
106
  "devDependencies": {
105
- "@babel/core": "7.28.4",
106
- "@babel/preset-env": "7.28.3",
107
+ "@babel/core": "7.28.5",
108
+ "@babel/preset-env": "7.28.5",
107
109
  "@redocly/openapi-cli": "1.0.0-beta.95",
108
110
  "@types/jest": "30.0.0",
109
111
  "@types/retry": "^0",
@@ -117,7 +119,7 @@
117
119
  "jest-junit": "16.0.0",
118
120
  "npm-check-updates": "16.7.10",
119
121
  "openapi-response-validator": "12.1.3",
120
- "openapi-typescript": "7.9.1",
122
+ "openapi-typescript": "7.10.1",
121
123
  "redis-mock": "0.56.3",
122
124
  "replace": "1.2.2",
123
125
  "standard-version": "9.5.0",
@@ -36,6 +36,7 @@ const dto = require('../dto');
36
36
  const shared = require('./lib/shared');
37
37
  const { BackendRequests, HTTPResponseError } = require('./lib/requests');
38
38
  const { SDKStateEnum, CacheKeyPrefixes } = require('./common');
39
+ const { extractStatusCodeFromError } = require('../utils');
39
40
 
40
41
  const TRACESTATE_KEY_CALLBACK_START_TS = 'tx_callback_start_ts';
41
42
 
@@ -1075,6 +1076,13 @@ class InboundTransfersModel {
1075
1076
  log.verbose(`putFxTransfersNotification attempt ${currentAttempt} succeeded for conversionId ${conversionId}`);
1076
1077
  resolve();
1077
1078
  } catch (err) {
1079
+ // Don't retry on 4xx errors
1080
+ let status = extractStatusCodeFromError(err);
1081
+ if (status && Number(status) >= 400 && Number(status) < 500) {
1082
+ log.warn(`putFxTransfersNotification attempt ${currentAttempt} got 4xx error (${status}), not retrying`, err);
1083
+ resolve();
1084
+ return;
1085
+ }
1078
1086
  log.warn(`putFxTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
1079
1087
  if (!operation.retry(err)) {
1080
1088
  log.verbose(`putFxTransfersNotification giving up after ${currentAttempt} attempts`);
@@ -1159,6 +1167,13 @@ class InboundTransfersModel {
1159
1167
  log.verbose(`putTransfersNotification attempt ${currentAttempt} succeeded for transferId ${transferId}`);
1160
1168
  resolve();
1161
1169
  } catch (err) {
1170
+ // Don't retry on 4xx errors
1171
+ let status = extractStatusCodeFromError(err);
1172
+ if (status && Number(status) >= 400 && Number(status) < 500) {
1173
+ log.warn(`putTransfersNotification attempt ${currentAttempt} got 4xx error (${status}), not retrying`, err);
1174
+ resolve();
1175
+ return;
1176
+ }
1162
1177
  this._logger.warn(`putTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
1163
1178
  if (!operation.retry(err)) {
1164
1179
  log.verbose(`putTransfersNotification giving up after ${currentAttempt} attempts`);
@@ -92,9 +92,28 @@ const generateTraceparent = (traceId = randomBytes(16).toString('hex'), traceFla
92
92
  return `00-${traceId}-${spanId}-${traceFlags.toLowerCase()}`;
93
93
  };
94
94
 
95
+ /** Extracts the HTTP status code from various error object structures
96
+ * @param {object} err - The error object to extract the status code from
97
+ * @returns {number|undefined} - The extracted status code, or undefined if not found
98
+ */
99
+ const extractStatusCodeFromError = (err) => {
100
+ let status = err?.response?.status || err?.statusCode;
101
+ if (!status && err?.response?.statusCode) {
102
+ status = err.response.statusCode;
103
+ }
104
+ if (!status && err?.status) {
105
+ status = err.status;
106
+ }
107
+ if (!status && err?.output?.statusCode) {
108
+ status = err.output.statusCode;
109
+ }
110
+ return status;
111
+ };
112
+
95
113
  module.exports = {
96
114
  createAuthClient,
97
115
  generateTraceparent,
98
116
  isValidTraceFlags,
99
- transformHeadersIsoToFspiop
117
+ transformHeadersIsoToFspiop,
118
+ extractStatusCodeFromError
100
119
  };
@@ -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
  });
@@ -20,10 +20,10 @@ module.exports = {
20
20
  clearMocks: true,
21
21
  coverageThreshold: {
22
22
  "global": {
23
- "branches": 75,
24
- "functions": 80,
25
- "lines": 80,
26
- "statements": -10
23
+ "branches": 14,
24
+ "functions": 44,
25
+ "lines": 30,
26
+ "statements": -525
27
27
  }
28
28
  },
29
29
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' })
@@ -34,15 +34,15 @@
34
34
  "test:xunit": "JEST_JUNIT_OUTPUT_DIR=../test/reports/ JEST_JUNIT_OUTPUT_NAME=xunit-command.xml yarn run test:unit -- --reporters=jest-junit",
35
35
  "test:integration": "jest --runInBand --passWithNoTests --testMatch '**/test/integration/**/*.test.ts' ",
36
36
  "test:coverage": "jest --passWithNoTests --coverage --testMatch '**/test/unit/**/*.test.ts'",
37
- "test:coverage-check:skip": "jest --coverage --testMatch '**/test/unit/**/*.test.ts'",
37
+ "test:coverage-check": "jest --runInBand --coverage --testMatch '**/test/unit/**/*.test.ts'",
38
38
  "dep:check": "ncu -e 2",
39
39
  "dep:update": "ncu -u",
40
40
  "release": "standard-version --skip.changelog --releaseCommitMessageFormat 'chore(release): {{currentTag}} [skip ci]'",
41
41
  "snapshot": "standard-version --no-verify --skip.changelog --prerelease snapshot --releaseCommitMessageFormat 'chore(snapshot): {{currentTag}}'"
42
42
  },
43
43
  "dependencies": {
44
- "@mojaloop/api-snippets": "18.2.0",
45
- "@mojaloop/central-services-shared": "18.34.1",
44
+ "@mojaloop/api-snippets": "18.2.1",
45
+ "@mojaloop/central-services-shared": "18.34.3",
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:^",
@@ -51,22 +51,22 @@
51
51
  "convict": "6.2.4",
52
52
  "express": "4.21.2",
53
53
  "openapi-backend": "5.15.0",
54
- "redis": "5.8.3",
54
+ "redis": "5.9.0",
55
55
  "swagger-ui-express": "5.0.1",
56
56
  "yamljs": "0.3.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@eslint/compat": "1.4.0",
59
+ "@eslint/compat": "1.4.1",
60
60
  "@types/convict": "6.1.6",
61
- "@types/express": "5.0.3",
61
+ "@types/express": "5.0.5",
62
62
  "@types/jest": "30.0.0",
63
- "@types/node": "24.7.0",
63
+ "@types/node": "24.10.0",
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.3",
69
+ "@typescript-eslint/parser": "8.46.3",
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
  },