@mojaloop/sdk-scheme-adapter 24.11.0-csi-1680.0 → 24.11.0-snapshot.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/.grype.yaml +1 -0
- package/.ncurc.yaml +3 -1
- package/.yarn/cache/{@babel-core-npm-7.28.3-fb967e901c-0faded84ed.zip → @babel-core-npm-7.28.4-cb5fd966cc-0593295241.zip} +0 -0
- package/.yarn/cache/{@babel-helpers-npm-7.28.3-8e4849da45-6d39031bf0.zip → @babel-helpers-npm-7.28.4-d9f7567704-5a70a82e19.zip} +0 -0
- package/.yarn/cache/@babel-parser-npm-7.28.4-e1b2cbaf6c-f54c46213e.zip +0 -0
- package/.yarn/cache/@babel-traverse-npm-7.28.4-ffade2903a-c3099364b7.zip +0 -0
- package/.yarn/cache/@babel-types-npm-7.28.4-7f16191818-db50bf257a.zip +0 -0
- package/.yarn/cache/@grpc-proto-loader-npm-0.8.0-b53ddeb647-216813bdca.zip +0 -0
- package/.yarn/cache/{@hapi-hapi-npm-21.4.2-b5f92c52c3-efe9025469.zip → @hapi-hapi-npm-21.4.3-3dc5e89aa0-f23bda02b4.zip} +0 -0
- package/.yarn/cache/@hapi-shot-npm-6.0.2-c6ccc15f52-8715f5759c.zip +0 -0
- package/.yarn/cache/{@hapi-subtext-npm-8.1.0-ea59196b68-3f7bf0c689.zip → @hapi-subtext-npm-8.1.1-8aefc21cfb-9ce8251d5e.zip} +0 -0
- package/.yarn/cache/@jest-transform-npm-30.1.2-14cfbd16d5-aec6c6a46f.zip +0 -0
- package/.yarn/cache/@jridgewell-remapping-npm-2.3.5-df8dacc063-c2bb01856e.zip +0 -0
- package/.yarn/cache/{@mojaloop-central-services-error-handling-npm-13.1.0-07ff108f3f-64e80a9ba2.zip → @mojaloop-central-services-error-handling-npm-13.1.1-990790968e-a5d8a46f3c.zip} +0 -0
- package/.yarn/cache/@mojaloop-central-services-error-handling-npm-13.1.2-680b8f106e-1357eefd24.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-logger-npm-11.9.3-7669d33c28-58cca19ace.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-metrics-npm-12.7.1-a82eece473-0aa6374c0e.zip +0 -0
- package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.30.6-d528dafb13-a2f47b26cb.zip → @mojaloop-central-services-shared-npm-18.32.1-82382b67a6-6f8a3ab043.zip} +0 -0
- package/.yarn/cache/{@mojaloop-central-services-shared-npm-18.30.7-291fa1aea3-aee239baa2.zip → @mojaloop-central-services-shared-npm-18.33.0-eee3a0674c-98d4f0e2ce.zip} +0 -0
- package/.yarn/cache/{@mojaloop-event-sdk-npm-14.6.1-a36281071d-5652aa9087.zip → @mojaloop-event-sdk-npm-14.7.0-dfe9fa1933-59adbf133d.zip} +0 -0
- package/.yarn/cache/@mojaloop-ml-number-npm-11.3.0-9858cadff5-8435794709.zip +0 -0
- package/.yarn/cache/{@mojaloop-ml-schema-transformer-lib-npm-2.7.7-ad2e66700a-0325beb4f9.zip → @mojaloop-ml-schema-transformer-lib-npm-2.7.8-270774b6ee-06bb19a304.zip} +0 -0
- package/.yarn/cache/{@mojaloop-sdk-standard-components-npm-19.16.4-59956a0e05-3feb521c69.zip → @mojaloop-sdk-standard-components-npm-19.17.0-0519957b97-95fef19fc4.zip} +0 -0
- package/.yarn/cache/{@redis-bloom-npm-5.8.1-139b45b8e4-f691b1dce2.zip → @redis-bloom-npm-5.8.2-1972c61f30-99ec4f127b.zip} +0 -0
- package/.yarn/cache/{@redis-client-npm-5.8.1-66d46a9ca1-329d76de06.zip → @redis-client-npm-5.8.2-68c2d31768-653ba2d0ef.zip} +0 -0
- package/.yarn/cache/{@redis-json-npm-5.8.1-1374d9e2de-9eabbf9a2c.zip → @redis-json-npm-5.8.2-99129c657f-2877cd93b7.zip} +0 -0
- package/.yarn/cache/{@redis-search-npm-5.8.1-ebc7760a31-a5e12dd2c7.zip → @redis-search-npm-5.8.2-c7014522ef-6cc0499a11.zip} +0 -0
- package/.yarn/cache/{@redis-time-series-npm-5.8.1-1f5e30ede4-c9440ce935.zip → @redis-time-series-npm-5.8.2-94d4c69124-a775817380.zip} +0 -0
- package/.yarn/cache/@rollup-rollup-linux-x64-musl-npm-4.50.2-1d19f7f518-10.zip +0 -0
- package/.yarn/cache/@types-node-npm-24.5.1-e2de7d4e53-1dd21dffe0.zip +0 -0
- package/.yarn/cache/@types-retry-npm-0.12.5-f1986a76a6-3fb6bf9183.zip +0 -0
- package/.yarn/cache/{@typescript-eslint-eslint-plugin-npm-8.39.1-8ad46b0385-446050aa43.zip → @typescript-eslint-eslint-plugin-npm-8.44.0-3a3d745bcf-38d0491d96.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-parser-npm-8.39.1-e931b25728-ff45ce7635.zip → @typescript-eslint-parser-npm-8.44.0-9be86aa2f8-8c7ddabf46.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-project-service-npm-8.39.1-f6db73ca22-1970633d1a.zip → @typescript-eslint-project-service-npm-8.44.0-3208cc873e-400b4981e6.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-scope-manager-npm-8.39.1-bf78e0253c-8874f74790.zip → @typescript-eslint-scope-manager-npm-8.44.0-02a051c9d1-5dae4a8386.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-tsconfig-utils-npm-8.39.1-e46dac00aa-38c1e19825.zip → @typescript-eslint-tsconfig-utils-npm-8.44.0-de2d92d917-c8535d481d.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-type-utils-npm-8.39.1-41cbec8085-1195d65970.zip → @typescript-eslint-type-utils-npm-8.44.0-4b4c61deae-513c6d3719.zip} +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.44.0-89c4325651-9e28c95feb.zip +0 -0
- package/.yarn/cache/{@typescript-eslint-typescript-estree-npm-8.39.1-eb0cf5436f-07ed9d7ab4.zip → @typescript-eslint-typescript-estree-npm-8.44.0-122f9245db-e2e579b15c.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-utils-npm-8.39.1-a6c63e4cf7-39bb105f26.zip → @typescript-eslint-utils-npm-8.44.0-fc612e2915-436e21e3d0.zip} +0 -0
- package/.yarn/cache/{@typescript-eslint-visitor-keys-npm-8.39.1-d0b0654c5b-6d4e4d0b19.zip → @typescript-eslint-visitor-keys-npm-8.44.0-131d4d0e8f-09b008b14f.zip} +0 -0
- package/.yarn/cache/{babel-jest-npm-30.0.5-8bced40b9f-39a36b8648.zip → babel-jest-npm-30.1.2-2d68b3440b-9697119fe4.zip} +0 -0
- package/.yarn/cache/bignumber.js-npm-9.3.1-d784181dd0-1be0372bf0.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.2-f2cdf74d0a-258210c403.zip +0 -0
- package/.yarn/cache/iconv-lite-npm-0.7.0-89105876e3-5bfc897fed.zip +0 -0
- package/.yarn/cache/{jest-haste-map-npm-30.0.5-ff2b66456e-3539359589.zip → jest-haste-map-npm-30.1.0-8189548adb-bd39053fe1.zip} +0 -0
- package/.yarn/cache/{jest-worker-npm-30.0.5-dcae728924-04d9a58ddb.zip → jest-worker-npm-30.1.0-b4a01545e6-cc09d2ce86.zip} +0 -0
- package/.yarn/cache/joi-npm-18.0.1-f286682573-d8e391c0e9.zip +0 -0
- package/.yarn/cache/{openapi-backend-npm-5.14.0-231377503b-1bd3e6cb71.zip → openapi-backend-npm-5.15.0-b7b193973a-f7bdc40ac6.zip} +0 -0
- package/.yarn/cache/{protobufjs-npm-7.5.3-a54566937a-3e412d2e2f.zip → protobufjs-npm-7.5.4-4d6f681551-88d677bb6f.zip} +0 -0
- package/.yarn/cache/{raw-body-npm-3.0.0-cd8403b401-2443429bbb.zip → raw-body-npm-3.0.1-cbb0b09e07-3cc63e1541.zip} +0 -0
- package/.yarn/cache/{redis-npm-5.8.1-201a0a72a3-26d97c6ddf.zip → redis-npm-5.8.2-9c493c0c47-a7635cedf2.zip} +0 -0
- package/.yarn/cache/{ts-jest-npm-29.4.1-ab76d85d32-6aed48232c.zip → ts-jest-npm-29.4.2-1fc50073bc-09494224db.zip} +0 -0
- package/.yarn/cache/{undici-types-npm-7.10.0-cd8324b9eb-1f3fe77793.zip → undici-types-npm-7.12.0-af0c725921-4a0f927c98.zip} +0 -0
- package/.yarn/install-state.gz +0 -0
- package/.yarn/releases/{yarn-4.9.2.cjs → yarn-4.9.4.cjs} +358 -358
- package/.yarnrc.yml +1 -1
- package/CHANGELOG.md +37 -0
- package/modules/api-svc/package.json +15 -13
- package/modules/api-svc/src/InboundServer/handlers.js +26 -8
- package/modules/api-svc/src/config.js +17 -1
- package/modules/api-svc/src/lib/model/InboundTransfersModel.js +146 -6
- package/modules/api-svc/src/lib/model/OutboundTransfersModel.js +1 -1
- package/modules/api-svc/test/__mocks__/redis.js +5 -2
- package/modules/api-svc/test/unit/inboundApi/handlers-iso20022.test.js +3 -1
- package/modules/api-svc/test/unit/inboundApi/handlers.test.js +4 -2
- package/modules/api-svc/test/unit/lib/model/InboundTransfersModel.test.js +481 -117
- package/modules/outbound-command-event-handler/package.json +8 -8
- package/modules/outbound-domain-event-handler/package.json +7 -7
- package/modules/private-shared-lib/package.json +7 -7
- package/package.json +6 -6
- package/{sbom-v24.10.8.csv → sbom-v24.10.11.csv} +224 -224
- package/.yarn/cache/@grpc-proto-loader-npm-0.7.15-889e15aec1-2e2b33ace8.zip +0 -0
- package/.yarn/cache/@hapi-hapi-npm-21.4.0-2644a983d1-d49ae44142.zip +0 -0
- package/.yarn/cache/@hapi-shot-npm-6.0.1-2553675f4f-6eb387f9c6.zip +0 -0
- package/.yarn/cache/@hapi-topo-npm-5.1.0-5e0b776809-084bfa6470.zip +0 -0
- package/.yarn/cache/@jest-transform-npm-30.0.5-90874ed0b8-2b3e0bc39a.zip +0 -0
- package/.yarn/cache/@mojaloop-central-services-metrics-npm-12.6.0-6353d00803-e55c70c0b1.zip +0 -0
- package/.yarn/cache/@rollup-rollup-linux-x64-musl-npm-4.45.1-255fc04506-10.zip +0 -0
- package/.yarn/cache/@sideway-address-npm-4.1.5-a3852745c8-c4c73ac033.zip +0 -0
- package/.yarn/cache/@sideway-formula-npm-3.0.1-ee371b2ddf-8d3ee7f80d.zip +0 -0
- package/.yarn/cache/@sideway-pinpoint-npm-2.0.0-66d94e687e-1ed2180012.zip +0 -0
- package/.yarn/cache/@types-node-npm-24.2.1-00ab09acd1-cdfa7b30b2.zip +0 -0
- package/.yarn/cache/@typescript-eslint-eslint-plugin-npm-8.32.1-4a9716e105-442205dd4e.zip +0 -0
- package/.yarn/cache/@typescript-eslint-parser-npm-8.32.1-4842816d93-3c2ab90fec.zip +0 -0
- package/.yarn/cache/@typescript-eslint-scope-manager-npm-8.32.1-7708347a5f-f81f71bd88.zip +0 -0
- package/.yarn/cache/@typescript-eslint-type-utils-npm-8.32.1-e98f19d598-e50a6f2a16.zip +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.32.1-ded19751b6-3a310e4baf.zip +0 -0
- package/.yarn/cache/@typescript-eslint-types-npm-8.39.1-8cea531133-8013f4f48a.zip +0 -0
- package/.yarn/cache/@typescript-eslint-typescript-estree-npm-8.32.1-5eacb17d12-8b956ce05b.zip +0 -0
- package/.yarn/cache/@typescript-eslint-utils-npm-8.32.1-8a5bff5552-9383cea185.zip +0 -0
- package/.yarn/cache/@typescript-eslint-visitor-keys-npm-8.32.1-780bd4dae9-a1cbfbdac8.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.0-4ee4b4bbd1-73d57d7ed8.zip +0 -0
- package/.yarn/cache/dotenv-npm-17.2.1-33fbb0afbc-8fde672d1c.zip +0 -0
- package/.yarn/cache/joi-npm-17.13.3-866dad5bc8-4c150db0c8.zip +0 -0
- package/.yarn/cache/joi-npm-18.0.0-1ebac7eadf-568004d69f.zip +0 -0
- package/.yarn/cache/openapi-backend-npm-5.13.0-03ae1ecf2c-a8b1d0d167.zip +0 -0
- package/.yarn/cache/yaml-npm-2.8.0-01747dd315-7d4bd9c10d.zip +0 -0
- package/audit/k6/package.json +0 -17
- package/docker/k666/package.json +0 -17
package/.yarnrc.yml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
1
|
# Changelog: [mojaloop/sdk-scheme-adapter](https://github.com/mojaloop/sdk-scheme-adapter)
|
|
2
|
+
### [24.10.11](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.10.10...v24.10.11) (2025-09-15)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
* ilp decimal calculation ([#612](https://github.com/mojaloop/sdk-scheme-adapter/issues/612)) ([77cdd19](https://github.com/mojaloop/sdk-scheme-adapter/commit/77cdd19dc2543f9bd115da4da55e1d3266d21e67))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Chore
|
|
11
|
+
|
|
12
|
+
* grype exception ([#613](https://github.com/mojaloop/sdk-scheme-adapter/issues/613)) ([464f4fd](https://github.com/mojaloop/sdk-scheme-adapter/commit/464f4fd95feec2953106839c134296d2f51f05b3))
|
|
13
|
+
* **sbom:** update sbom [skip ci] ([051986c](https://github.com/mojaloop/sdk-scheme-adapter/commit/051986ccc8bb51b616200239cb1727598fd29f74))
|
|
14
|
+
|
|
15
|
+
### [24.10.10](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.10.9...v24.10.10) (2025-08-27)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **csi-1708:** updated deps ([#607](https://github.com/mojaloop/sdk-scheme-adapter/issues/607)) ([42f7baf](https://github.com/mojaloop/sdk-scheme-adapter/commit/42f7baf2e828e4eea2eb4edc41cd847d4e15651c))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Chore
|
|
24
|
+
|
|
25
|
+
* **sbom:** update sbom [skip ci] ([c05eac1](https://github.com/mojaloop/sdk-scheme-adapter/commit/c05eac19196b19b1b3ff3c6cafc0744207e4b43d))
|
|
26
|
+
|
|
27
|
+
### [24.10.9](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.10.8...v24.10.9) (2025-08-18)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* **csi-1680:** changed PATCH transfers/fxTransfers logic of sending notifications ([#605](https://github.com/mojaloop/sdk-scheme-adapter/issues/605)) ([749d84a](https://github.com/mojaloop/sdk-scheme-adapter/commit/749d84a785a571b7f401b2e98f2852bc2ae175d8))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Chore
|
|
36
|
+
|
|
37
|
+
* **sbom:** update sbom [skip ci] ([071b19e](https://github.com/mojaloop/sdk-scheme-adapter/commit/071b19e185aff5e3f5e17681c4032d85b8bc35e8))
|
|
38
|
+
|
|
2
39
|
### [24.10.8](https://github.com/mojaloop/sdk-scheme-adapter/compare/v24.10.7...v24.10.8) (2025-08-11)
|
|
3
40
|
|
|
4
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/sdk-scheme-adapter-api-svc",
|
|
3
|
-
"version": "21.0.0-snapshot.
|
|
3
|
+
"version": "21.0.0-snapshot.59",
|
|
4
4
|
"description": "An adapter for connecting to Mojaloop API enabled switches.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -65,20 +65,20 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@koa/cors": "5.0.0",
|
|
67
67
|
"@mojaloop/api-snippets": "18.1.1",
|
|
68
|
-
"@mojaloop/central-services-error-handling": "13.1.
|
|
69
|
-
"@mojaloop/central-services-logger": "11.9.
|
|
70
|
-
"@mojaloop/central-services-metrics": "12.
|
|
71
|
-
"@mojaloop/central-services-shared": "18.
|
|
72
|
-
"@mojaloop/event-sdk": "14.
|
|
68
|
+
"@mojaloop/central-services-error-handling": "13.1.2",
|
|
69
|
+
"@mojaloop/central-services-logger": "11.9.3",
|
|
70
|
+
"@mojaloop/central-services-metrics": "12.7.1",
|
|
71
|
+
"@mojaloop/central-services-shared": "18.33.0",
|
|
72
|
+
"@mojaloop/event-sdk": "14.7.0",
|
|
73
73
|
"@mojaloop/logging-bc-client-lib": "0.5.8",
|
|
74
|
-
"@mojaloop/ml-schema-transformer-lib": "2.7.
|
|
74
|
+
"@mojaloop/ml-schema-transformer-lib": "2.7.8",
|
|
75
75
|
"@mojaloop/sdk-scheme-adapter-private-shared-lib": "workspace:^",
|
|
76
|
-
"@mojaloop/sdk-standard-components": "19.
|
|
76
|
+
"@mojaloop/sdk-standard-components": "19.17.0",
|
|
77
77
|
"ajv": "8.17.1",
|
|
78
|
-
"axios": "1.
|
|
78
|
+
"axios": "1.12.2",
|
|
79
79
|
"body-parser": "2.2.0",
|
|
80
80
|
"co-body": "6.2.0",
|
|
81
|
-
"dotenv": "17.2.
|
|
81
|
+
"dotenv": "17.2.2",
|
|
82
82
|
"env-var": "7.5.0",
|
|
83
83
|
"express": "4.21.2",
|
|
84
84
|
"fast-json-patch": "3.1.1",
|
|
@@ -96,17 +96,19 @@
|
|
|
96
96
|
"prom-client": "15.1.3",
|
|
97
97
|
"promise-timeout": "1.3.0",
|
|
98
98
|
"random-word-slugs": "0.1.7",
|
|
99
|
-
"redis": "5.8.
|
|
99
|
+
"redis": "5.8.2",
|
|
100
|
+
"retry": "^0.13.1",
|
|
100
101
|
"uuidv4": "6.2.13",
|
|
101
102
|
"ws": "8.18.3"
|
|
102
103
|
},
|
|
103
104
|
"devDependencies": {
|
|
104
|
-
"@babel/core": "7.28.
|
|
105
|
+
"@babel/core": "7.28.4",
|
|
105
106
|
"@babel/preset-env": "7.28.3",
|
|
106
107
|
"@redocly/openapi-cli": "1.0.0-beta.95",
|
|
107
108
|
"@types/jest": "30.0.0",
|
|
109
|
+
"@types/retry": "^0",
|
|
108
110
|
"axios-mock-adapter": "2.1.0",
|
|
109
|
-
"babel-jest": "30.
|
|
111
|
+
"babel-jest": "30.1.2",
|
|
110
112
|
"eslint": "9.15.0",
|
|
111
113
|
"eslint-config-airbnb-base": "15.0.0",
|
|
112
114
|
"eslint-plugin-import": "2.32.0",
|
|
@@ -729,12 +729,22 @@ const patchTransfersById = async (ctx) => {
|
|
|
729
729
|
// use the transfers model to execute asynchronous stages with the switch
|
|
730
730
|
const model = createInboundTransfersModel(ctx);
|
|
731
731
|
|
|
732
|
-
//
|
|
733
|
-
|
|
732
|
+
// kick off an asynchronous operation to handle the request
|
|
733
|
+
(async () => {
|
|
734
|
+
try {
|
|
735
|
+
// sends notification to the payee fsp
|
|
736
|
+
const response = await model.sendNotificationToPayee(req.data, idValue);
|
|
737
|
+
|
|
738
|
+
// log the result
|
|
739
|
+
ctx.state.logger.isDebugEnabled && ctx.state.logger.push({ response }).
|
|
740
|
+
debug('Inbound transfers model handled PATCH /transfers/{ID} request');
|
|
741
|
+
} catch (err) {
|
|
742
|
+
ctx.state.logger.isErrorEnabled && ctx.state.logger.push({ err }).error('Error handling PATCH /transfers/{ID}');
|
|
743
|
+
}
|
|
744
|
+
})();
|
|
734
745
|
|
|
735
|
-
|
|
736
|
-
ctx.
|
|
737
|
-
debug('Inbound transfers model handled PATCH /transfers/{ID} request');
|
|
746
|
+
ctx.response.status = ReturnCodes.OK.CODE;
|
|
747
|
+
ctx.response.body = '';
|
|
738
748
|
};
|
|
739
749
|
|
|
740
750
|
/**
|
|
@@ -1080,10 +1090,18 @@ const patchFxTransfersById = async (ctx) => {
|
|
|
1080
1090
|
const data = { ...ctx.request.body };
|
|
1081
1091
|
const idValue = ctx.state.path.params.ID;
|
|
1082
1092
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1093
|
+
(async () => {
|
|
1094
|
+
const model = createInboundTransfersModel(ctx);
|
|
1095
|
+
try {
|
|
1096
|
+
const response = await model.sendFxPutNotificationToBackend(data, idValue);
|
|
1097
|
+
ctx.state.logger.push({ response }).debug('Inbound Transfers model handled PATCH /fxTransfers/{ID} request');
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
ctx.state.logger.push({ err }).error('Error handling PATCH /fxTransfers/{ID}');
|
|
1100
|
+
}
|
|
1101
|
+
})();
|
|
1085
1102
|
|
|
1086
|
-
ctx.
|
|
1103
|
+
ctx.response.status = ReturnCodes.OK.CODE;
|
|
1104
|
+
ctx.response.body = '';
|
|
1087
1105
|
};
|
|
1088
1106
|
|
|
1089
1107
|
/**
|
|
@@ -264,5 +264,21 @@ module.exports = {
|
|
|
264
264
|
|
|
265
265
|
// Redis key ttl when stored in the cache, if value is used as zero it will
|
|
266
266
|
// persist throughout the session , value used is in seconds
|
|
267
|
-
redisCacheTtl: env.get('REDIS_CACHE_TTL').default('0').asInt()
|
|
267
|
+
redisCacheTtl: env.get('REDIS_CACHE_TTL').default('0').asInt(),
|
|
268
|
+
|
|
269
|
+
backendRequestRetry: {
|
|
270
|
+
enabled: env.get('BACKEND_REQUEST_RETRY_ENABLED').default('true').asBool(),
|
|
271
|
+
maxRetries: env.get('BACKEND_REQUEST_RETRY_MAX_RETRIES').default('5').asIntPositive(),
|
|
272
|
+
retryDelayMs: env.get('BACKEND_REQUEST_RETRY_DELAY_MS').default('1000').asIntPositive(),
|
|
273
|
+
maxRetryDelayMs: env.get('BACKEND_REQUEST_RETRY_MAX_DELAY_MS').default('10000').asIntPositive(),
|
|
274
|
+
backoffFactor: env.get('BACKEND_REQUEST_RETRY_BACKOFF_FACTOR').default('2').asIntPositive(),
|
|
275
|
+
},
|
|
276
|
+
getTransferRequestRetry: {
|
|
277
|
+
enabled: env.get('GET_TRANSFER_REQUEST_RETRY_ENABLED').default('false').asBool(),
|
|
278
|
+
maxRetries: env.get('GET_TRANSFER_REQUEST_RETRY_MAX_RETRIES').default('3').asIntPositive(),
|
|
279
|
+
retryDelayMs: env.get('GET_TRANSFER_REQUEST_RETRY_DELAY_MS').default('1000').asIntPositive(),
|
|
280
|
+
maxRetryDelayMs: env.get('GET_TRANSFER_REQUEST_RETRY_MAX_DELAY_MS').default('10000').asIntPositive(),
|
|
281
|
+
backoffFactor: env.get('GET_TRANSFER_REQUEST_RETRY_BACKOFF_FACTOR').default('2').asIntPositive(),
|
|
282
|
+
},
|
|
283
|
+
patchNotificationGraceTimeMs: env.get('PATCH_NOTIFICATION_GRACE_TIME_MS').default('15000').asIntPositive(),
|
|
268
284
|
};
|
|
@@ -30,6 +30,7 @@ const safeStringify = require('fast-safe-stringify');
|
|
|
30
30
|
const { MojaloopRequests, Ilp, Errors } = require('@mojaloop/sdk-standard-components');
|
|
31
31
|
const FSPIOPTransferStateEnum = require('@mojaloop/central-services-shared').Enum.Transfers.TransferState;
|
|
32
32
|
const FSPIOPBulkTransferStateEnum = require('@mojaloop/central-services-shared').Enum.Transfers.BulkTransferState;
|
|
33
|
+
const retry = require('retry');
|
|
33
34
|
|
|
34
35
|
const dto = require('../dto');
|
|
35
36
|
const shared = require('./lib/shared');
|
|
@@ -52,6 +53,9 @@ class InboundTransfersModel {
|
|
|
52
53
|
this._reserveNotification = config.reserveNotification;
|
|
53
54
|
this._allowDifferentTransferTransactionId = config.allowDifferentTransferTransactionId;
|
|
54
55
|
this._supportedCurrencies = config.supportedCurrencies;
|
|
56
|
+
this._patchNotificationGraceTimeMs = config.patchNotificationGraceTimeMs;
|
|
57
|
+
this._getTransferRequestRetry = config.getTransferRequestRetry;
|
|
58
|
+
this._backendRequestRetry = config.backendRequestRetry;
|
|
55
59
|
|
|
56
60
|
this._mojaloopRequests = new MojaloopRequests({
|
|
57
61
|
logger: this._logger,
|
|
@@ -489,6 +493,61 @@ class InboundTransfersModel {
|
|
|
489
493
|
this.data.currentState = response.transferState || (this._reserveNotification ? SDKStateEnum.RESERVED : SDKStateEnum.COMPLETED);
|
|
490
494
|
|
|
491
495
|
await this._save();
|
|
496
|
+
|
|
497
|
+
// --- PATCH NOTIFICATION TIMER LOGIC ---
|
|
498
|
+
// Set a timer to trigger GET /transfers/{ID} if sendNotificationToPayee is not called in time
|
|
499
|
+
if (this._patchNotificationGraceTimeMs > 0) {
|
|
500
|
+
const transferId = prepareRequest.transferId;
|
|
501
|
+
const cacheKey = `patchNotificationSent_${transferId}`;
|
|
502
|
+
// Mark as not notified yet
|
|
503
|
+
await this._cache.set(cacheKey, false, Math.ceil(this._patchNotificationGraceTimeMs / 1000) + 5);
|
|
504
|
+
|
|
505
|
+
setTimeout(async () => {
|
|
506
|
+
try {
|
|
507
|
+
this._logger.isInfoEnabled && this._logger.push({ transferId }).info('Patch notification grace time expired, attempting GET /transfers/{ID}');
|
|
508
|
+
const notified = await this._cache.get(cacheKey);
|
|
509
|
+
|
|
510
|
+
if (!notified) {
|
|
511
|
+
// Subscribe to transfer callback channel
|
|
512
|
+
const transferKey = `tf_${transferId}`;
|
|
513
|
+
const unsubscribeTimeout = this._getTransferRequestRetry?.retryDelayMs || 1000;
|
|
514
|
+
let gotCallback = false;
|
|
515
|
+
|
|
516
|
+
// Subscribe for one message with a timeout
|
|
517
|
+
const messagePromise = this._cache.subscribeToOneMessageWithTimer(transferKey, Math.ceil(unsubscribeTimeout / 1000));
|
|
518
|
+
// Kick off GET /transfers/{ID}
|
|
519
|
+
await this._mojaloopRequests.getTransfers(transferId, sourceFspId, headers);
|
|
520
|
+
|
|
521
|
+
// Retry logic
|
|
522
|
+
let attempts = 0;
|
|
523
|
+
const maxAttempts = (this._getTransferRequestRetry?.maxRetries || 3);
|
|
524
|
+
const retryDelay = this._getTransferRequestRetry?.retryDelayMs || 1000;
|
|
525
|
+
while (attempts < maxAttempts && !gotCallback) {
|
|
526
|
+
try {
|
|
527
|
+
const message = await messagePromise;
|
|
528
|
+
if (message && message.data) {
|
|
529
|
+
gotCallback = true;
|
|
530
|
+
// Mark as notified to prevent duplicate notification
|
|
531
|
+
await this._cache.set(cacheKey, true, 60);
|
|
532
|
+
// Send notification to payee
|
|
533
|
+
await this.sendNotificationToPayee(message.data.body, transferId);
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
} catch (err) {
|
|
537
|
+
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error('Error while waiting for transfer callback in patch notification grace timer logic');
|
|
538
|
+
}
|
|
539
|
+
attempts++;
|
|
540
|
+
if (!gotCallback && attempts < maxAttempts) {
|
|
541
|
+
await this._mojaloopRequests.getTransfers(transferId, sourceFspId, headers);
|
|
542
|
+
await new Promise(r => setTimeout(r, retryDelay));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
} catch (err) {
|
|
547
|
+
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error('Error in patch notification grace timer logic');
|
|
548
|
+
}
|
|
549
|
+
}, this._patchNotificationGraceTimeMs);
|
|
550
|
+
}
|
|
492
551
|
return res;
|
|
493
552
|
} catch(err) {
|
|
494
553
|
this._logger.isErrorEnabled && this._logger.push({ err }).error(`Error in prepareTransfer: ${prepareRequest?.transferId}`);
|
|
@@ -956,7 +1015,7 @@ class InboundTransfersModel {
|
|
|
956
1015
|
async sendFxPutNotificationToBackend(body, conversionId) {
|
|
957
1016
|
const log = this._logger.child({ conversionId });
|
|
958
1017
|
try {
|
|
959
|
-
log.verbose('sendFxPutNotificationToBackend
|
|
1018
|
+
log.verbose('sendFxPutNotificationToBackend incoming payload: ', { body });
|
|
960
1019
|
this.data = await this.loadFxState(conversionId);
|
|
961
1020
|
|
|
962
1021
|
if(!this.data) {
|
|
@@ -978,12 +1037,51 @@ class InboundTransfersModel {
|
|
|
978
1037
|
|
|
979
1038
|
const responseBody = {
|
|
980
1039
|
conversionState: body.conversionState, // one of ABORTED, COMMITTED, RESERVED
|
|
981
|
-
completedTimestamp: body.completedTimestamp
|
|
1040
|
+
completedTimestamp: body.completedTimestamp,
|
|
982
1041
|
};
|
|
983
|
-
log.verbose('sendFxPutNotificationToBackend
|
|
984
|
-
|
|
1042
|
+
log.verbose('sendFxPutNotificationToBackend body sent to cc: ', { responseBody });
|
|
1043
|
+
|
|
1044
|
+
const { enabled, maxRetries, retryDelayMs, maxRetryDelayMs, backoffFactor } = this._backendRequestRetry || {};
|
|
1045
|
+
let res;
|
|
1046
|
+
const shouldRetry = enabled !== false; // default to true if not set
|
|
1047
|
+
|
|
1048
|
+
if (shouldRetry) {
|
|
1049
|
+
const operation = retry.operation({
|
|
1050
|
+
retries: maxRetries || 5,
|
|
1051
|
+
factor: backoffFactor || 2,
|
|
1052
|
+
minTimeout: retryDelayMs || 1000,
|
|
1053
|
+
maxTimeout: maxRetryDelayMs || 10000,
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
await new Promise((resolve) => {
|
|
1057
|
+
operation.attempt(async (currentAttempt) => {
|
|
1058
|
+
try {
|
|
1059
|
+
res = await this._backendRequests.putFxTransfersNotification(responseBody, conversionId);
|
|
1060
|
+
if ((res && (res.status === 200 || res.statusCode === 200)) || res === true) {
|
|
1061
|
+
return resolve();
|
|
1062
|
+
}
|
|
1063
|
+
log.warn(`putFxTransfersNotification attempt ${currentAttempt} failed, retrying...`);
|
|
1064
|
+
if (!operation.retry(new Error('Non-200 response'))) {
|
|
1065
|
+
resolve();
|
|
1066
|
+
}
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
log.warn(`putFxTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
|
|
1069
|
+
if (!operation.retry(err)) {
|
|
1070
|
+
resolve();
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
});
|
|
985
1075
|
|
|
986
|
-
|
|
1076
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1077
|
+
log.error(`putFxTransfersNotification failed after ${operation.attempts()} attempts`);
|
|
1078
|
+
}
|
|
1079
|
+
} else {
|
|
1080
|
+
res = await this._backendRequests.putFxTransfersNotification(responseBody, conversionId);
|
|
1081
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1082
|
+
log.error('putFxTransfersNotification failed');
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
987
1085
|
return res;
|
|
988
1086
|
} catch (err) {
|
|
989
1087
|
log.error('error in sendFxPutNotificationToBackend: ', err);
|
|
@@ -1026,7 +1124,49 @@ class InboundTransfersModel {
|
|
|
1026
1124
|
|
|
1027
1125
|
await this._save();
|
|
1028
1126
|
|
|
1029
|
-
const
|
|
1127
|
+
const { enabled, maxRetries, retryDelayMs, maxRetryDelayMs, backoffFactor } = this._backendRequestRetry || {};
|
|
1128
|
+
let res;
|
|
1129
|
+
const shouldRetry = enabled !== false; // default to true if not set
|
|
1130
|
+
|
|
1131
|
+
if (shouldRetry) {
|
|
1132
|
+
const operation = retry.operation({
|
|
1133
|
+
retries: maxRetries || 5,
|
|
1134
|
+
factor: backoffFactor || 2,
|
|
1135
|
+
minTimeout: retryDelayMs || 1000,
|
|
1136
|
+
maxTimeout: maxRetryDelayMs || 10000,
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
await new Promise((resolve) => {
|
|
1140
|
+
operation.attempt(async (currentAttempt) => {
|
|
1141
|
+
try {
|
|
1142
|
+
res = await this._backendRequests.putTransfersNotification(this.data, transferId);
|
|
1143
|
+
if ((res && (res.status === 200 || res.statusCode === 200)) || res === true) {
|
|
1144
|
+
const cacheKey = `patchNotificationSent_${transferId}`;
|
|
1145
|
+
await this._cache.set(cacheKey, true, 60);
|
|
1146
|
+
return resolve();
|
|
1147
|
+
}
|
|
1148
|
+
this._logger.warn(`putTransfersNotification attempt ${currentAttempt} failed, retrying...`);
|
|
1149
|
+
if (!operation.retry(new Error('Non-200 response'))) {
|
|
1150
|
+
resolve();
|
|
1151
|
+
}
|
|
1152
|
+
} catch (err) {
|
|
1153
|
+
this._logger.warn(`putTransfersNotification attempt ${currentAttempt} threw error, retrying...`, err);
|
|
1154
|
+
if (!operation.retry(err)) {
|
|
1155
|
+
resolve();
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1162
|
+
this._logger.error(`putTransfersNotification failed after ${operation.attempts()} attempts`);
|
|
1163
|
+
}
|
|
1164
|
+
} else {
|
|
1165
|
+
res = await this._backendRequests.putTransfersNotification(this.data, transferId);
|
|
1166
|
+
if ((res && (res.status !== 200 && res.statusCode !== 200)) && res !== true) {
|
|
1167
|
+
this._logger.error('putTransfersNotification failed');
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1030
1170
|
return res;
|
|
1031
1171
|
} catch (err) {
|
|
1032
1172
|
this._logger.isErrorEnabled && this._logger.push({ err, transferId }).error(`Error notifying backend of final transfer state equal to: ${body.transferState}`);
|
|
@@ -1473,7 +1473,7 @@ class OutboundTransfersModel {
|
|
|
1473
1473
|
log.isVerboseEnabled && log.verbose(`Transfer model state machine transition completed in state: ${this.stateMachine.state}. Recursing to handle next transition.`);
|
|
1474
1474
|
return this.run();
|
|
1475
1475
|
} catch (err) {
|
|
1476
|
-
log.
|
|
1476
|
+
log.error('error running outbound transfer model: ', err);
|
|
1477
1477
|
|
|
1478
1478
|
// as this function is recursive, we dont want to error the state machine multiple times
|
|
1479
1479
|
if (this.data.currentState !== States.ERRORED) {
|
|
@@ -56,8 +56,11 @@ class RedisClient extends redisMock.RedisClient {
|
|
|
56
56
|
process.nextTick(() => this.events.emit(...args));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Note: This Redis mock implementation does not support options like TTL (time-to-live).
|
|
61
|
+
*/
|
|
62
|
+
set(key, value) {
|
|
63
|
+
return promisify(super.set.bind(this))(key, value);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
get(...args) {
|
|
@@ -315,7 +315,7 @@ describe('Inbound API handlers transforming incoming ISO20022 message bodies', (
|
|
|
315
315
|
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
test('calls `prepareTransfer` with the expected arguments.', async () => {
|
|
318
|
+
test('calls `prepareTransfer` with the expected arguments and sets 200 status.', async () => {
|
|
319
319
|
const transferRequestSpy = jest.spyOn(Model.prototype, 'sendNotificationToPayee');
|
|
320
320
|
|
|
321
321
|
await expect(handlers['/transfers/{ID}'].patch(mockContext)).resolves.toBe(undefined);
|
|
@@ -324,6 +324,8 @@ describe('Inbound API handlers transforming incoming ISO20022 message bodies', (
|
|
|
324
324
|
expect(transferRequestSpy.mock.calls[0][0]).not.toBeUndefined();
|
|
325
325
|
expect(transferRequestSpy.mock.calls[0][0]).not.toEqual(isoBodies.patchTransfersRequest);
|
|
326
326
|
expect(transferRequestSpy.mock.calls[0][0].transferState).toBe('COMMITTED');
|
|
327
|
+
expect(mockContext.response.status).toBe(200);
|
|
328
|
+
console.log('Response body:', mockContext.response);
|
|
327
329
|
});
|
|
328
330
|
});
|
|
329
331
|
|
|
@@ -624,12 +624,13 @@ describe('Inbound API handlers:', () => {
|
|
|
624
624
|
};
|
|
625
625
|
});
|
|
626
626
|
|
|
627
|
-
test('calls `model.sendNotificationToPayee with expected arguments', async () => {
|
|
627
|
+
test('calls `model.sendNotificationToPayee` with expected arguments and responds 200', async () => {
|
|
628
628
|
const notificationSpy = jest.spyOn(Model.prototype, 'sendNotificationToPayee');
|
|
629
629
|
|
|
630
630
|
await expect(handlers['/transfers/{ID}'].patch(mockNotificationMessage)).resolves.toBe(undefined);
|
|
631
631
|
expect(notificationSpy).toHaveBeenCalledTimes(1);
|
|
632
632
|
expect(notificationSpy.mock.calls[0][1]).toBe(mockNotificationMessage.state.path.params.ID);
|
|
633
|
+
expect(mockNotificationMessage.response.status).toBe(200);
|
|
633
634
|
});
|
|
634
635
|
|
|
635
636
|
});
|
|
@@ -1075,12 +1076,13 @@ describe('Inbound API handlers:', () => {
|
|
|
1075
1076
|
};
|
|
1076
1077
|
});
|
|
1077
1078
|
|
|
1078
|
-
test('calls `model.sendFxPutNotificationToBackend with expected arguments', async () => {
|
|
1079
|
+
test('calls `model.sendFxPutNotificationToBackend` with expected arguments and responds 200', async () => {
|
|
1079
1080
|
const notificationSpy = jest.spyOn(Model.prototype, 'sendFxPutNotificationToBackend');
|
|
1080
1081
|
|
|
1081
1082
|
await expect(handlers['/fxTransfers/{ID}'].patch(mockNotificationMessage)).resolves.toBe(undefined);
|
|
1082
1083
|
expect(notificationSpy).toHaveBeenCalledTimes(1);
|
|
1083
1084
|
expect(notificationSpy.mock.calls[0][1]).toBe(mockNotificationMessage.state.path.params.ID);
|
|
1085
|
+
expect(mockNotificationMessage.response.status).toBe(200);
|
|
1084
1086
|
});
|
|
1085
1087
|
|
|
1086
1088
|
});
|