@mojaloop/central-services-shared 18.2.0 → 18.2.1-snapshot.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.
- package/CHANGELOG.md +0 -7
- package/package.json +9 -9
- package/src/constants.js +7 -0
- package/src/enums/accounts.js +3 -1
- package/src/enums/endpoints.js +10 -1
- package/src/enums/events.js +6 -0
- package/src/enums/fx.js +38 -0
- package/src/enums/index.js +3 -1
- package/src/enums/kafka.js +40 -0
- package/src/index.d.ts +0 -7
- package/src/util/endpoints.js +14 -56
- package/src/util/hapi/plugins/headerValidation.js +4 -1
- package/src/util/headerValidation/index.js +4 -2
- package/src/util/index.js +0 -2
- package/src/util/kafka/index.js +4 -1
- package/src/util/request.js +2 -16
- package/test/unit/util/endpoints.test.js +13 -76
- package/test/util/helper.js +1 -29
- package/src/util/participants.js +0 -165
- package/test/unit/util/participants.test.js +0 -157
package/CHANGELOG.md
CHANGED
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
## [18.2.0](https://github.com/mojaloop/central-services-shared/compare/v18.1.3...v18.2.0) (2023-11-23)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
### Features
|
|
9
|
-
|
|
10
|
-
* **mojaloop/#3426:** add participant request caching, axios override and metrics ([#357](https://github.com/mojaloop/central-services-shared/issues/357)) ([c5329a2](https://github.com/mojaloop/central-services-shared/commit/c5329a2f92c63fd9c9319f07ae01e5d3a4f2c11b)), closes [mojaloop/#3426](https://github.com/mojaloop/project/issues/3426)
|
|
11
|
-
|
|
12
5
|
### [18.1.3](https://github.com/mojaloop/central-services-shared/compare/v18.1.2...v18.1.3) (2023-11-02)
|
|
13
6
|
|
|
14
7
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-services-shared",
|
|
3
|
-
"version": "18.2.0",
|
|
3
|
+
"version": "18.2.1-snapshot.0",
|
|
4
4
|
"description": "Shared code for mojaloop central services",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -54,29 +54,29 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@hapi/catbox": "12.1.1",
|
|
56
56
|
"@hapi/catbox-memory": "5.0.1",
|
|
57
|
-
"axios": "1.6.
|
|
57
|
+
"axios": "1.6.7",
|
|
58
58
|
"clone": "2.1.2",
|
|
59
|
-
"dotenv": "16.
|
|
59
|
+
"dotenv": "16.4.5",
|
|
60
60
|
"env-var": "7.4.1",
|
|
61
61
|
"event-stream": "4.0.1",
|
|
62
|
-
"immutable": "4.3.
|
|
62
|
+
"immutable": "4.3.5",
|
|
63
63
|
"lodash": "4.17.21",
|
|
64
64
|
"mustache": "4.2.0",
|
|
65
|
-
"openapi-backend": "5.10.
|
|
65
|
+
"openapi-backend": "5.10.6",
|
|
66
66
|
"raw-body": "2.5.2",
|
|
67
67
|
"rc": "1.2.8",
|
|
68
68
|
"shins": "2.6.0",
|
|
69
69
|
"uuid4": "2.0.3",
|
|
70
70
|
"widdershins": "^4.0.1",
|
|
71
|
-
"yaml": "2.
|
|
71
|
+
"yaml": "2.4.0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@hapi/hapi": "21.3.
|
|
74
|
+
"@hapi/hapi": "21.3.3",
|
|
75
75
|
"@hapi/joi": "17.1.1",
|
|
76
76
|
"audit-ci": "^6.6.1",
|
|
77
77
|
"base64url": "3.0.1",
|
|
78
78
|
"chance": "1.1.11",
|
|
79
|
-
"npm-check-updates": "16.14.
|
|
79
|
+
"npm-check-updates": "16.14.15",
|
|
80
80
|
"nyc": "15.1.0",
|
|
81
81
|
"pre-commit": "1.2.2",
|
|
82
82
|
"proxyquire": "2.1.3",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"standard-version": "9.5.0",
|
|
88
88
|
"tap-spec": "^5.0.0",
|
|
89
89
|
"tap-xunit": "2.4.1",
|
|
90
|
-
"tape": "5.7.
|
|
90
|
+
"tape": "5.7.5",
|
|
91
91
|
"tapes": "4.1.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
package/src/constants.js
ADDED
package/src/enums/accounts.js
CHANGED
package/src/enums/endpoints.js
CHANGED
|
@@ -53,6 +53,9 @@ const FspEndpointTypes = {
|
|
|
53
53
|
FSPIOP_CALLBACK_URL_TRANSFER_POST: 'FSPIOP_CALLBACK_URL_TRANSFER_POST',
|
|
54
54
|
FSPIOP_CALLBACK_URL_TRANSFER_PUT: 'FSPIOP_CALLBACK_URL_TRANSFER_PUT',
|
|
55
55
|
FSPIOP_CALLBACK_URL_TRANSFER_ERROR: 'FSPIOP_CALLBACK_URL_TRANSFER_ERROR',
|
|
56
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_POST: 'FSPIOP_CALLBACK_URL_FX_TRANSFER_POST',
|
|
57
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT: 'FSPIOP_CALLBACK_URL_FX_TRANSFER_PUT',
|
|
58
|
+
FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR: 'FSPIOP_CALLBACK_URL_FX_TRANSFER_ERROR',
|
|
56
59
|
ALARM_NOTIFICATION_URL: 'ALARM_NOTIFICATION_URL',
|
|
57
60
|
ALARM_NOTIFICATION_TOPIC: 'ALARM_NOTIFICATION_TOPIC',
|
|
58
61
|
NET_DEBIT_CAP_THRESHOLD_BREACH_EMAIL: 'NET_DEBIT_CAP_THRESHOLD_BREACH_EMAIL',
|
|
@@ -60,6 +63,7 @@ const FspEndpointTypes = {
|
|
|
60
63
|
SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL: 'SETTLEMENT_TRANSFER_POSITION_CHANGE_EMAIL',
|
|
61
64
|
FSPIOP_CALLBACK_URL_QUOTES: 'FSPIOP_CALLBACK_URL_QUOTES',
|
|
62
65
|
FSPIOP_CALLBACK_URL_BULK_QUOTES: 'FSPIOP_CALLBACK_URL_BULK_QUOTES',
|
|
66
|
+
FSPIOP_CALLBACK_URL_FX_QUOTES: 'FSPIOP_CALLBACK_URL_FX_QUOTES',
|
|
63
67
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_POST',
|
|
64
68
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_PUT',
|
|
65
69
|
FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR: 'FSPIOP_CALLBACK_URL_BULK_TRANSFER_ERROR',
|
|
@@ -102,7 +106,6 @@ const FspEndpointTemplates = {
|
|
|
102
106
|
TRANSACTION_REQUEST_PUT_ERROR: '/transactionRequests/{{ID}}/error',
|
|
103
107
|
PARTICIPANT_ENDPOINTS_GET: '/participants/{{fsp}}/endpoints',
|
|
104
108
|
PARTICIPANTS_GET: '/participants/{{fsp}}',
|
|
105
|
-
PARTICIPANTS_POST: '/participants',
|
|
106
109
|
PARTIES_GET: '/parties/{{fsp}}',
|
|
107
110
|
PARTIES_PUT_ERROR: '/parties/{{partyIdType}}/{{partyIdentifier}}/error',
|
|
108
111
|
PARTIES_SUB_ID_PUT_ERROR: '/parties/{{partyIdType}}/{{partyIdentifier}}/{{partySubIdOrType}}/error',
|
|
@@ -111,6 +114,12 @@ const FspEndpointTemplates = {
|
|
|
111
114
|
ORACLE_PARTICIPANTS_TYPE_ID_SUB_ID: '/participants/{{partyIdType}}/{{partyIdentifier}}?partySubIdOrType={{partySubIdOrType}}',
|
|
112
115
|
ORACLE_PARTICIPANTS_TYPE_ID_CURRENCY_SUB_ID: '/participants/{{partyIdType}}/{{partyIdentifier}}?currency={{currency}}&partySubIdOrType={{partySubIdOrType}}',
|
|
113
116
|
ORACLE_PARTICIPANTS_BATCH: '/participants',
|
|
117
|
+
FX_TRANSFERS_POST: '/fxTransfers',
|
|
118
|
+
FX_TRANSFERS_PUT: '/{{fsp}}/fxTransfers/{{ID}}',
|
|
119
|
+
FX_TRANSFERS_PUT_ERROR: '/{{fsp}}/fxTransfers/{{ID}}/error',
|
|
120
|
+
FX_QUOTES_POST: '/fxQuotes',
|
|
121
|
+
FX_QUOTES_PUT: '/{{fsp}}/fxQuotes/{{ID}}',
|
|
122
|
+
FX_QUOTES_ERROR_PUT: '/fxQuotes/{{ID}}/error',
|
|
114
123
|
TRANSFERS_POST: '/transfers',
|
|
115
124
|
TRANSFERS_PUT: '/{{fsp}}/transfers/{{ID}}',
|
|
116
125
|
TRANSFERS_PUT_ERROR: '/{{fsp}}/transfers/{{ID}}/error',
|
package/src/enums/events.js
CHANGED
|
@@ -39,6 +39,7 @@ const Event = {
|
|
|
39
39
|
ENDPOINTCACHE: 'endpointcache',
|
|
40
40
|
EVENT: 'event',
|
|
41
41
|
FULFIL: 'fulfil',
|
|
42
|
+
FX_QUOTE: 'fxquote',
|
|
42
43
|
GET: 'get',
|
|
43
44
|
NOTIFICATION: 'notification',
|
|
44
45
|
ORACLE: 'oracle',
|
|
@@ -78,6 +79,11 @@ const Event = {
|
|
|
78
79
|
FAIL: 'fail',
|
|
79
80
|
FULFIL: 'fulfil',
|
|
80
81
|
FULFIL_DUPLICATE: 'fulfil-duplicate',
|
|
82
|
+
FX_ABORT: 'fx-abort',
|
|
83
|
+
FX_COMMIT: 'fx-commit',
|
|
84
|
+
FX_PREPARE: 'fx-prepare',
|
|
85
|
+
FX_REJECT: 'fx-reject',
|
|
86
|
+
FX_RESERVE: 'fx-reserve',
|
|
81
87
|
GET: 'get',
|
|
82
88
|
INITIATE: 'initiate',
|
|
83
89
|
LIMIT_ADJUSTMENT: 'limit-adjustment',
|
package/src/enums/fx.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*****
|
|
2
|
+
License
|
|
3
|
+
--------------
|
|
4
|
+
Copyright © 2017 Bill & Melinda Gates Foundation
|
|
5
|
+
The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
|
|
6
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
8
|
+
Contributors
|
|
9
|
+
--------------
|
|
10
|
+
This is the official list of the Mojaloop project contributors for this file.
|
|
11
|
+
Names of the original copyright holders (individuals or organizations)
|
|
12
|
+
should be listed with a '*' in the first column. People who have
|
|
13
|
+
contributed from an organization can be listed under the organization
|
|
14
|
+
that actually holds the copyright for their contributions (see the
|
|
15
|
+
Gates Foundation organization for an example). Those individuals should have
|
|
16
|
+
their names indented and be marked with a '-'. Email address can be added
|
|
17
|
+
optionally within square brackets <email>.
|
|
18
|
+
* Gates Foundation
|
|
19
|
+
- Name Surname <name.surname@gatesfoundation.com>
|
|
20
|
+
|
|
21
|
+
* Vijay Kumar Guthi <vijaya.guthi@infitx.com>
|
|
22
|
+
--------------
|
|
23
|
+
******/
|
|
24
|
+
|
|
25
|
+
const FxTransferType = {
|
|
26
|
+
PAYER_CONVERSION: 1,
|
|
27
|
+
PAYEE_CONVERSION: 2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const FxParticipantCurrencyType = {
|
|
31
|
+
SOURCE: 1,
|
|
32
|
+
TARGET: 2
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
FxTransferType,
|
|
37
|
+
FxParticipantCurrencyType
|
|
38
|
+
}
|
package/src/enums/index.js
CHANGED
|
@@ -32,6 +32,7 @@ const Transfers = require('./transfers')
|
|
|
32
32
|
const Events = require('./events')
|
|
33
33
|
const Kafka = require('./kafka')
|
|
34
34
|
const Tags = require('./tags')
|
|
35
|
+
const Fx = require('./fx')
|
|
35
36
|
|
|
36
37
|
module.exports = {
|
|
37
38
|
Accounts,
|
|
@@ -41,5 +42,6 @@ module.exports = {
|
|
|
41
42
|
Settlements,
|
|
42
43
|
Transfers,
|
|
43
44
|
Kafka,
|
|
44
|
-
Tags
|
|
45
|
+
Tags,
|
|
46
|
+
Fx
|
|
45
47
|
}
|
package/src/enums/kafka.js
CHANGED
|
@@ -142,6 +142,26 @@ const TopicMap = {
|
|
|
142
142
|
functionality: transferEventType.NOTIFICATION,
|
|
143
143
|
action: transferEventAction.EVENT
|
|
144
144
|
},
|
|
145
|
+
'fx-abort': {
|
|
146
|
+
functionality: transferEventType.NOTIFICATION,
|
|
147
|
+
action: transferEventAction.EVENT
|
|
148
|
+
},
|
|
149
|
+
'fx-commit': {
|
|
150
|
+
functionality: transferEventType.NOTIFICATION,
|
|
151
|
+
action: transferEventAction.EVENT
|
|
152
|
+
},
|
|
153
|
+
'fx-prepare': {
|
|
154
|
+
functionality: transferEventType.NOTIFICATION,
|
|
155
|
+
action: transferEventAction.EVENT
|
|
156
|
+
},
|
|
157
|
+
'fx-reject': {
|
|
158
|
+
functionality: transferEventType.NOTIFICATION,
|
|
159
|
+
action: transferEventAction.EVENT
|
|
160
|
+
},
|
|
161
|
+
'fx-reserve': {
|
|
162
|
+
functionality: transferEventType.NOTIFICATION,
|
|
163
|
+
action: transferEventAction.EVENT
|
|
164
|
+
},
|
|
145
165
|
'prepare-duplicate': {
|
|
146
166
|
functionality: transferEventType.NOTIFICATION,
|
|
147
167
|
action: transferEventAction.EVENT
|
|
@@ -192,6 +212,26 @@ const TopicMap = {
|
|
|
192
212
|
functionality: transferEventType.TRANSFER,
|
|
193
213
|
action: transferEventAction.POSITION
|
|
194
214
|
},
|
|
215
|
+
'fx-abort': {
|
|
216
|
+
functionality: transferEventType.TRANSFER,
|
|
217
|
+
action: transferEventAction.POSITION
|
|
218
|
+
},
|
|
219
|
+
'fx-commit': {
|
|
220
|
+
functionality: transferEventType.TRANSFER,
|
|
221
|
+
action: transferEventAction.POSITION
|
|
222
|
+
},
|
|
223
|
+
'fx-prepare': {
|
|
224
|
+
functionality: transferEventType.TRANSFER,
|
|
225
|
+
action: transferEventAction.POSITION
|
|
226
|
+
},
|
|
227
|
+
'fx-reject': {
|
|
228
|
+
functionality: transferEventType.TRANSFER,
|
|
229
|
+
action: transferEventAction.POSITION
|
|
230
|
+
},
|
|
231
|
+
'fx-reserve': {
|
|
232
|
+
functionality: transferEventType.TRANSFER,
|
|
233
|
+
action: transferEventAction.POSITION
|
|
234
|
+
},
|
|
195
235
|
prepare: {
|
|
196
236
|
functionality: transferEventType.TRANSFER,
|
|
197
237
|
action: transferEventAction.POSITION
|
package/src/index.d.ts
CHANGED
|
@@ -175,7 +175,6 @@ declare namespace CentralServicesShared {
|
|
|
175
175
|
TRANSACTION_REQUEST_PUT_ERROR: string;
|
|
176
176
|
PARTICIPANT_ENDPOINTS_GET: string;
|
|
177
177
|
PARTICIPANTS_GET: string;
|
|
178
|
-
PARTICIPANTS_POST: string;
|
|
179
178
|
PARTIES_GET: string;
|
|
180
179
|
PARTIES_PUT_ERROR: string;
|
|
181
180
|
PARTIES_SUB_ID_PUT_ERROR: string;
|
|
@@ -556,11 +555,6 @@ declare namespace CentralServicesShared {
|
|
|
556
555
|
getEndpointAndRender(switchUrl: string, fsp: string, endpointType: FspEndpointTypesEnum, path: string, options?: any): Promise<string>
|
|
557
556
|
}
|
|
558
557
|
|
|
559
|
-
interface Participants {
|
|
560
|
-
getParticipant(switchUrl: string, fsp: string): Promise<object>
|
|
561
|
-
initializeCache(policyOptions: object): Promise<boolean>
|
|
562
|
-
}
|
|
563
|
-
|
|
564
558
|
interface ProtocolVersionsType {
|
|
565
559
|
content: string,
|
|
566
560
|
accept: string
|
|
@@ -580,7 +574,6 @@ declare namespace CentralServicesShared {
|
|
|
580
574
|
|
|
581
575
|
interface Util {
|
|
582
576
|
Endpoints: Endpoints;
|
|
583
|
-
Participants: Participants;
|
|
584
577
|
Hapi: any;
|
|
585
578
|
Kafka: Kafka;
|
|
586
579
|
OpenapiBackend: any;
|
package/src/util/endpoints.js
CHANGED
|
@@ -26,17 +26,19 @@
|
|
|
26
26
|
'use strict'
|
|
27
27
|
|
|
28
28
|
const Logger = require('@mojaloop/central-services-logger')
|
|
29
|
+
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
29
30
|
const Catbox = require('@hapi/catbox')
|
|
30
31
|
const CatboxMemory = require('@hapi/catbox-memory')
|
|
32
|
+
const Mustache = require('mustache')
|
|
31
33
|
const { Map } = require('immutable')
|
|
32
|
-
|
|
34
|
+
|
|
35
|
+
const { ERROR_MESSAGES } = require('../constants')
|
|
33
36
|
const Enum = require('../enums')
|
|
37
|
+
const Http = require('./http')
|
|
38
|
+
const request = require('./request')
|
|
39
|
+
|
|
34
40
|
const partition = 'endpoint-cache'
|
|
35
41
|
const clientOptions = { partition }
|
|
36
|
-
const Mustache = require('mustache')
|
|
37
|
-
const request = require('./request')
|
|
38
|
-
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
39
|
-
const Metrics = require('@mojaloop/central-services-metrics')
|
|
40
42
|
|
|
41
43
|
let client
|
|
42
44
|
let policy
|
|
@@ -51,11 +53,6 @@ let switchEndpoint
|
|
|
51
53
|
* @returns {object} endpointMap Returns the object containing the endpoints for given fsp id
|
|
52
54
|
*/
|
|
53
55
|
const fetchEndpoints = async (fsp) => {
|
|
54
|
-
const histTimer = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
55
|
-
'fetchParticipants',
|
|
56
|
-
'fetchParticipants - Metrics for fetchParticipants',
|
|
57
|
-
['success']
|
|
58
|
-
).startTimer()
|
|
59
56
|
try {
|
|
60
57
|
Logger.isDebugEnabled && Logger.debug(`[fsp=${fsp}] ~ participantEndpointCache::fetchEndpoints := Refreshing the cache for FSP: ${fsp}`)
|
|
61
58
|
const defaultHeaders = Http.SwitchDefaultHeaders(Enum.Http.HeaderResources.SWITCH, Enum.Http.HeaderResources.PARTICIPANTS, Enum.Http.HeaderResources.SWITCH)
|
|
@@ -72,10 +69,8 @@ const fetchEndpoints = async (fsp) => {
|
|
|
72
69
|
})
|
|
73
70
|
}
|
|
74
71
|
Logger.isDebugEnabled && Logger.debug(`[fsp=${fsp}] ~ participantEndpointCache::fetchEndpoints := Returning the endpoints: ${JSON.stringify(endpointMap)}`)
|
|
75
|
-
histTimer({ success: true })
|
|
76
72
|
return endpointMap
|
|
77
73
|
} catch (e) {
|
|
78
|
-
histTimer({ success: false })
|
|
79
74
|
Logger.isErrorEnabled && Logger.error(`participantEndpointCache::fetchEndpoints:: ERROR:'${e}'`)
|
|
80
75
|
}
|
|
81
76
|
}
|
|
@@ -88,7 +83,7 @@ const fetchEndpoints = async (fsp) => {
|
|
|
88
83
|
* @function initializeCache
|
|
89
84
|
*
|
|
90
85
|
* @description This initializes the cache for endpoints
|
|
91
|
-
* @param {object} policyOptions The Endpoint_Cache_Config for the Cache being stored
|
|
86
|
+
* @param {object} policyOptions The Endpoint_Cache_Config for the Cache being stored
|
|
92
87
|
|
|
93
88
|
* @returns {boolean} Returns true on successful initialization of the cache, throws error on failures
|
|
94
89
|
*/
|
|
@@ -121,31 +116,17 @@ exports.initializeCache = async (policyOptions) => {
|
|
|
121
116
|
* @returns {string} - Returns the endpoint, throws error if failure occurs
|
|
122
117
|
*/
|
|
123
118
|
exports.getEndpoint = async (switchUrl, fsp, endpointType, options = {}) => {
|
|
124
|
-
const histTimer = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
125
|
-
'getEndpoint',
|
|
126
|
-
'getEndpoint - Metrics for getEndpoint with cache hit rate',
|
|
127
|
-
['success', 'hit']
|
|
128
|
-
).startTimer()
|
|
129
119
|
switchEndpoint = switchUrl
|
|
130
120
|
Logger.isDebugEnabled && Logger.debug(`participantEndpointCache::getEndpoint::endpointType - ${endpointType}`)
|
|
131
121
|
try {
|
|
132
|
-
// If a service passes in `getDecoratedValue` as true, then an object
|
|
133
|
-
// { value, cached, report } is returned, where value is the cached value,
|
|
134
|
-
// cached is null on a cache miss.
|
|
135
122
|
const endpoints = await policy.get(fsp)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else {
|
|
140
|
-
histTimer({ success: true, hit: true })
|
|
141
|
-
}
|
|
142
|
-
return Mustache.render(new Map(endpoints.value).get(endpointType), options)
|
|
123
|
+
const endpoint = new Map(endpoints).get(endpointType)
|
|
124
|
+
if (!endpoint) {
|
|
125
|
+
throw new Error(ERROR_MESSAGES.noFspEndpointInCache)
|
|
143
126
|
}
|
|
144
|
-
|
|
145
|
-
return Mustache.render(new Map(endpoints).get(endpointType), options)
|
|
127
|
+
return Mustache.render(endpoint, options)
|
|
146
128
|
} catch (err) {
|
|
147
|
-
|
|
148
|
-
Logger.isErrorEnabled && Logger.error(`participantEndpointCache::getEndpoint:: ERROR:'${err}'`)
|
|
129
|
+
Logger.isErrorEnabled && Logger.error(`participantEndpointCache::getEndpoint:: ERROR:'${err}' [fsp: ${fsp}, endpointType: ${endpointType}]`)
|
|
149
130
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
150
131
|
}
|
|
151
132
|
}
|
|
@@ -164,35 +145,14 @@ exports.getEndpoint = async (switchUrl, fsp, endpointType, options = {}) => {
|
|
|
164
145
|
* @returns {string} - Returns the rendered endpoint, throws error if failure occurs
|
|
165
146
|
*/
|
|
166
147
|
exports.getEndpointAndRender = async (switchUrl, fsp, endpointType, path = '', options) => {
|
|
167
|
-
const histTimer = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
168
|
-
'getEndpointAndRender',
|
|
169
|
-
'getEndpoint - Metrics for getEndpointAndRender with cache hit rate',
|
|
170
|
-
['success', 'hit']
|
|
171
|
-
).startTimer()
|
|
172
148
|
switchEndpoint = switchUrl
|
|
173
149
|
Logger.isDebugEnabled && Logger.debug(`participantEndpointCache::getEndpointAndRender::endpointType - ${endpointType}`)
|
|
174
150
|
try {
|
|
175
|
-
// If a service passes in `getDecoratedValue` as true, then an object
|
|
176
|
-
// { value, cached, report } is returned, where value is the cached value,
|
|
177
|
-
// `cached` is null on a cache miss.
|
|
178
151
|
const endpoints = await policy.get(fsp)
|
|
179
|
-
if ('value' in endpoints && 'cached' in endpoints) {
|
|
180
|
-
if (endpoints.cached === null) {
|
|
181
|
-
histTimer({ success: true, hit: false })
|
|
182
|
-
} else {
|
|
183
|
-
histTimer({ success: true, hit: true })
|
|
184
|
-
}
|
|
185
|
-
const endpoint = new Map(endpoints.value).get(endpointType)
|
|
186
|
-
const renderedEndpoint = (endpoint === undefined) ? endpoint : endpoint + path
|
|
187
|
-
return Mustache.render(renderedEndpoint, options)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
152
|
const endpoint = new Map(endpoints).get(endpointType)
|
|
191
153
|
const renderedEndpoint = (endpoint === undefined) ? endpoint : endpoint + path
|
|
192
|
-
histTimer({ success: true, hit: false })
|
|
193
154
|
return Mustache.render(renderedEndpoint, options)
|
|
194
155
|
} catch (err) {
|
|
195
|
-
histTimer({ success: false, hit: false })
|
|
196
156
|
Logger.isErrorEnabled && Logger.error(`participantEndpointCache::getEndpointAndRender:: ERROR:'${err}'`)
|
|
197
157
|
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
198
158
|
}
|
|
@@ -207,7 +167,5 @@ exports.getEndpointAndRender = async (switchUrl, fsp, endpointType, path = '', o
|
|
|
207
167
|
*/
|
|
208
168
|
exports.stopCache = async () => {
|
|
209
169
|
Logger.isDebugEnabled && Logger.debug('participantEndpointCache::stopCache::Stopping the cache')
|
|
210
|
-
|
|
211
|
-
return client.stop()
|
|
212
|
-
}
|
|
170
|
+
return client.stop()
|
|
213
171
|
}
|
|
@@ -18,11 +18,14 @@ const defaultProtocolResources = [
|
|
|
18
18
|
'bulkTransfers',
|
|
19
19
|
'bulkQuotes',
|
|
20
20
|
'transactionRequests',
|
|
21
|
-
'authorizations'
|
|
21
|
+
'authorizations',
|
|
22
|
+
'fxQuotes',
|
|
23
|
+
'fxTransfers'
|
|
22
24
|
]
|
|
23
25
|
|
|
24
26
|
const defaultProtocolVersions = [
|
|
25
27
|
...protocolVersions.ONE,
|
|
28
|
+
...protocolVersions.TWO,
|
|
26
29
|
protocolVersions.anyVersion
|
|
27
30
|
]
|
|
28
31
|
|
|
@@ -5,12 +5,14 @@ const _ = require('lodash')
|
|
|
5
5
|
|
|
6
6
|
const protocolVersions = {
|
|
7
7
|
anyVersion: Symbol('Any'),
|
|
8
|
-
ONE: ['1', '1.0', '1.1']
|
|
8
|
+
ONE: ['1', '1.0', '1.1'],
|
|
9
|
+
TWO: ['2', '2.0']
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
const protocolVersionsMap = [
|
|
12
13
|
{ key: '1', value: '0' },
|
|
13
|
-
{ key: '1', value: '1' }
|
|
14
|
+
{ key: '1', value: '1' },
|
|
15
|
+
{ key: '2', value: '0' }
|
|
14
16
|
]
|
|
15
17
|
|
|
16
18
|
// Some convenience functions for generating regexes for header matching
|
package/src/util/index.js
CHANGED
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
const _ = require('lodash')
|
|
30
30
|
const Kafka = require('./kafka')
|
|
31
31
|
const Endpoints = require('./endpoints')
|
|
32
|
-
const Participants = require('./participants')
|
|
33
32
|
const Request = require('./request')
|
|
34
33
|
const Http = require('./http')
|
|
35
34
|
const Hapi = require('./hapi')
|
|
@@ -236,7 +235,6 @@ module.exports = {
|
|
|
236
235
|
getCircularReplacer,
|
|
237
236
|
filterExtensions,
|
|
238
237
|
Kafka,
|
|
239
|
-
Participants,
|
|
240
238
|
Endpoints,
|
|
241
239
|
Request,
|
|
242
240
|
Http,
|
package/src/util/kafka/index.js
CHANGED
|
@@ -306,7 +306,10 @@ const proceed = async (defaultKafkaConfig, params, opts) => {
|
|
|
306
306
|
if (fromSwitch) {
|
|
307
307
|
message.value.to = message.value.from
|
|
308
308
|
message.value.from = Enum.Http.Headers.FSPIOP.SWITCH.value
|
|
309
|
-
if (message.value.content.headers)
|
|
309
|
+
if (message.value.content.headers) {
|
|
310
|
+
message.value.content.headers[Enum.Http.Headers.FSPIOP.SOURCE] = message.value.from
|
|
311
|
+
message.value.content.headers[Enum.Http.Headers.FSPIOP.DESTINATION] = message.value.to
|
|
312
|
+
}
|
|
310
313
|
}
|
|
311
314
|
if (typeof toDestination === 'string') {
|
|
312
315
|
message.value.to = toDestination
|
package/src/util/request.js
CHANGED
|
@@ -62,24 +62,11 @@ request.defaults.httpAgent = new http.Agent({ keepAlive: true })
|
|
|
62
62
|
* @param {object | undefined} span a span for event logging if this request is within a span
|
|
63
63
|
* @param {object | undefined} jwsSigner the jws signer for signing the requests
|
|
64
64
|
* @param {SendRequestProtocolVersions | undefined} protocolVersions the config for Protocol versions to be used
|
|
65
|
-
* @param {object} axiosRequestOptionsOverride axios request options to override https://axios-http.com/docs/req_config
|
|
66
65
|
*
|
|
67
66
|
*@return {Promise<any>} The response for the request being sent or error object with response included
|
|
68
67
|
*/
|
|
69
68
|
|
|
70
|
-
const sendRequest = async (
|
|
71
|
-
url,
|
|
72
|
-
headers,
|
|
73
|
-
source,
|
|
74
|
-
destination,
|
|
75
|
-
method = enums.Http.RestMethods.GET,
|
|
76
|
-
payload = undefined,
|
|
77
|
-
responseType = enums.Http.ResponseTypes.JSON,
|
|
78
|
-
span = undefined,
|
|
79
|
-
jwsSigner = undefined,
|
|
80
|
-
protocolVersions = undefined,
|
|
81
|
-
axiosRequestOptionsOverride = {}
|
|
82
|
-
) => {
|
|
69
|
+
const sendRequest = async (url, headers, source, destination, method = enums.Http.RestMethods.GET, payload = undefined, responseType = enums.Http.ResponseTypes.JSON, span = undefined, jwsSigner = undefined, protocolVersions = undefined) => {
|
|
83
70
|
const histTimerEnd = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
84
71
|
'sendRequest',
|
|
85
72
|
`sending ${method} request to: ${url} from: ${source} to: ${destination}`,
|
|
@@ -106,8 +93,7 @@ const sendRequest = async (
|
|
|
106
93
|
method,
|
|
107
94
|
headers: transformedHeaders,
|
|
108
95
|
data: payload,
|
|
109
|
-
responseType
|
|
110
|
-
...axiosRequestOptionsOverride
|
|
96
|
+
responseType
|
|
111
97
|
}
|
|
112
98
|
// if jwsSigner is passed then sign the request
|
|
113
99
|
if (jwsSigner != null && typeof (jwsSigner) === 'object') {
|
|
@@ -1,38 +1,26 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const Test = require('tapes')(require('tape'))
|
|
4
|
-
const
|
|
4
|
+
const Mustache = require('mustache')
|
|
5
|
+
const Catbox = require('@hapi/catbox')
|
|
6
|
+
const Logger = require('@mojaloop/central-services-logger')
|
|
5
7
|
const Sinon = require('sinon')
|
|
8
|
+
|
|
9
|
+
const src = '../../../src'
|
|
6
10
|
const Cache = require(`${src}/util/endpoints`)
|
|
7
11
|
const request = require(`${src}/util/request`)
|
|
8
|
-
const Catbox = require('@hapi/catbox')
|
|
9
12
|
const Config = require('../../util/config')
|
|
10
13
|
const Http = require(`${src}/util`).Http
|
|
11
14
|
const Enum = require(`${src}`).Enum
|
|
12
|
-
const Mustache = require('mustache')
|
|
13
15
|
const Helper = require('../../util/helper')
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
16
|
+
const { ERROR_MESSAGES } = require('../../../src/constants')
|
|
17
|
+
|
|
18
|
+
const { FSPIOP_CALLBACK_URL_TRANSFER_PUT } = Enum.EndPoints.FspEndpointTypes
|
|
17
19
|
|
|
18
20
|
Test('Cache Test', cacheTest => {
|
|
19
21
|
let sandbox
|
|
20
22
|
|
|
21
23
|
cacheTest.beforeEach(async test => {
|
|
22
|
-
Metrics.setup({
|
|
23
|
-
INSTRUMENTATION: {
|
|
24
|
-
METRICS: {
|
|
25
|
-
DISABLED: false,
|
|
26
|
-
config: {
|
|
27
|
-
timeout: 5000,
|
|
28
|
-
prefix: 'moja_ml_',
|
|
29
|
-
defaultLabels: {
|
|
30
|
-
serviceName: 'ml-service'
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
24
|
sandbox = Sinon.createSandbox()
|
|
37
25
|
sandbox.stub(request, 'sendRequest')
|
|
38
26
|
sandbox.stub(Http, 'SwitchDefaultHeaders').returns(Helper.defaultHeaders())
|
|
@@ -68,29 +56,6 @@ Test('Cache Test', cacheTest => {
|
|
|
68
56
|
}
|
|
69
57
|
})
|
|
70
58
|
|
|
71
|
-
getEndpointTest.test('return the endpoint if catbox returns decoratedValue object', async (test) => {
|
|
72
|
-
const fsp = 'fsp'
|
|
73
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
74
|
-
const endpointType = FSPIOP_CALLBACK_URL_TRANSFER_PUT
|
|
75
|
-
const expected = 'http://localhost:1080/transfers/97b01bd3-b223-415b-b37b-ab5bef9bdbed'
|
|
76
|
-
|
|
77
|
-
await Cache.initializeCache({
|
|
78
|
-
...Config.ENDPOINT_CACHE_CONFIG,
|
|
79
|
-
getDecoratedValue: true
|
|
80
|
-
})
|
|
81
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve(Helper.getEndPointsResponse))
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const result = await Cache.getEndpoint(Config.ENDPOINT_SOURCE_URL, fsp, endpointType, { transferId: '97b01bd3-b223-415b-b37b-ab5bef9bdbed' })
|
|
85
|
-
test.equal(result, expected, 'The results match')
|
|
86
|
-
await Cache.stopCache()
|
|
87
|
-
test.end()
|
|
88
|
-
} catch (err) {
|
|
89
|
-
test.fail('Error thrown', err)
|
|
90
|
-
test.end()
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
|
|
94
59
|
getEndpointTest.test('return throw an error if array not returned in response object', async (test) => {
|
|
95
60
|
const fsp = 'fsp'
|
|
96
61
|
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
@@ -102,13 +67,12 @@ Test('Cache Test', cacheTest => {
|
|
|
102
67
|
try {
|
|
103
68
|
await Cache.getEndpoint(Config.ENDPOINT_SOURCE_URL, fsp, endpointType, { transferId: '97b01bd3-b223-415b-b37b-ab5bef9bdbed' })
|
|
104
69
|
test.fail('should throw error')
|
|
105
|
-
await Cache.stopCache()
|
|
106
|
-
test.end()
|
|
107
70
|
} catch (e) {
|
|
108
71
|
test.ok(e instanceof Error)
|
|
109
|
-
|
|
110
|
-
test.end()
|
|
72
|
+
test.ok(e.message.includes(ERROR_MESSAGES.noFspEndpointInCache))
|
|
111
73
|
}
|
|
74
|
+
await Cache.stopCache()
|
|
75
|
+
test.end()
|
|
112
76
|
})
|
|
113
77
|
|
|
114
78
|
getEndpointTest.test('throw error', async (test) => {
|
|
@@ -159,34 +123,6 @@ Test('Cache Test', cacheTest => {
|
|
|
159
123
|
}
|
|
160
124
|
})
|
|
161
125
|
|
|
162
|
-
getEndpointAndRenderTest.test('return the rendered endpoint if catbox returns decoratedValue object', async (test) => {
|
|
163
|
-
const fsp = 'fsp'
|
|
164
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
165
|
-
const endpointType = FSPIOP_CALLBACK_URL_TRANSFER_PUT
|
|
166
|
-
const expected = 'http://localhost:1080/transfers/97b01bd3-b223-415b-b37b-ab5bef9bdbed'
|
|
167
|
-
|
|
168
|
-
await Cache.initializeCache({
|
|
169
|
-
...Config.ENDPOINT_CACHE_CONFIG,
|
|
170
|
-
getDecoratedValue: true
|
|
171
|
-
})
|
|
172
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve(Helper.getEndpointAndRenderResponse))
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
const result = await Cache.getEndpointAndRender(
|
|
176
|
-
Config.ENDPOINT_SOURCE_URL,
|
|
177
|
-
fsp,
|
|
178
|
-
endpointType,
|
|
179
|
-
'transfers/{{transferId}}',
|
|
180
|
-
{ transferId: '97b01bd3-b223-415b-b37b-ab5bef9bdbed' })
|
|
181
|
-
test.equal(result, expected, 'The results match')
|
|
182
|
-
await Cache.stopCache()
|
|
183
|
-
test.end()
|
|
184
|
-
} catch (err) {
|
|
185
|
-
test.fail('Error thrown', err)
|
|
186
|
-
test.end()
|
|
187
|
-
}
|
|
188
|
-
})
|
|
189
|
-
|
|
190
126
|
getEndpointAndRenderTest.test('return throw an error if array not returned in response object', async (test) => {
|
|
191
127
|
const fsp = 'fsp'
|
|
192
128
|
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANT_ENDPOINTS_GET, { fsp })
|
|
@@ -249,7 +185,8 @@ Test('Cache Test', cacheTest => {
|
|
|
249
185
|
|
|
250
186
|
initializeCacheTest.test('should throw error', async (test) => {
|
|
251
187
|
try {
|
|
252
|
-
sandbox.stub(
|
|
188
|
+
Catbox.Client = sandbox.stub()
|
|
189
|
+
Catbox.Client.throws(new Error())
|
|
253
190
|
await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
254
191
|
test.fail('should throw')
|
|
255
192
|
test.end()
|
package/test/util/helper.js
CHANGED
|
@@ -40,33 +40,6 @@ const defaultHeaders = (destination, resource, source, version = '1.0') => {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const getParticipantsResponse = {
|
|
44
|
-
data: [
|
|
45
|
-
{
|
|
46
|
-
name: 'fsp1',
|
|
47
|
-
id: 'http://central-ledger/participants/fsp1',
|
|
48
|
-
created: '"2023-11-07T21:52:25.000Z"',
|
|
49
|
-
isActive: 1,
|
|
50
|
-
links: { self: 'http://central-ledger/participants/fsp1' },
|
|
51
|
-
accounts: [
|
|
52
|
-
{ id: 7, ledgerAccountType: 'POSITION', currency: 'USD', isActive: 1, createdDate: null, createdBy: 'unknown' },
|
|
53
|
-
{ id: 8, ledgerAccountType: 'SETTLEMENT', currency: 'USD', isActive: 1, createdDate: null, createdBy: 'unknown' }
|
|
54
|
-
]
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: 'fsp2',
|
|
58
|
-
id: 'http://central-ledger/participants/fsp2',
|
|
59
|
-
created: '"2023-11-07T21:52:25.000Z"',
|
|
60
|
-
isActive: 1,
|
|
61
|
-
links: { self: 'http://central-ledger/participants/fsp2' },
|
|
62
|
-
accounts: [
|
|
63
|
-
{ id: 9, ledgerAccountType: 'POSITION', currency: 'USD', isActive: 1, createdDate: null, createdBy: 'unknown' },
|
|
64
|
-
{ id: 10, ledgerAccountType: 'SETTLEMENT', currency: 'USD', isActive: 1, createdDate: null, createdBy: 'unknown' }
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
|
|
70
43
|
const getEndPointsResponse = {
|
|
71
44
|
data: [
|
|
72
45
|
{
|
|
@@ -105,6 +78,5 @@ module.exports = {
|
|
|
105
78
|
defaultHeaders,
|
|
106
79
|
generateProtocolHeader,
|
|
107
80
|
getEndPointsResponse,
|
|
108
|
-
getEndpointAndRenderResponse
|
|
109
|
-
getParticipantsResponse
|
|
81
|
+
getEndpointAndRenderResponse
|
|
110
82
|
}
|
package/src/util/participants.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/*****
|
|
2
|
-
License
|
|
3
|
-
--------------
|
|
4
|
-
Copyright © 2017 Bill & Melinda Gates Foundation
|
|
5
|
-
The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
|
|
6
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
-
Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
8
|
-
Contributors
|
|
9
|
-
--------------
|
|
10
|
-
This is the official list of the Mojaloop project contributors for this file.
|
|
11
|
-
Names of the original copyright holders (individuals or organizations)
|
|
12
|
-
should be listed with a '*' in the first column. People who have
|
|
13
|
-
contributed from an organization can be listed under the organization
|
|
14
|
-
that actually holds the copyright for their contributions (see the
|
|
15
|
-
Gates Foundation organization for an example). Those individuals should have
|
|
16
|
-
their names indented and be marked with a '-'. Email address can be added
|
|
17
|
-
optionally within square brackets <email>.
|
|
18
|
-
* Gates Foundation
|
|
19
|
-
* Name Surname <name.surname@gatesfoundation.com>
|
|
20
|
-
|
|
21
|
-
* Kevin Leyow <kevin.leyow@infitx.com>
|
|
22
|
-
--------------
|
|
23
|
-
******/
|
|
24
|
-
|
|
25
|
-
'use strict'
|
|
26
|
-
|
|
27
|
-
const Logger = require('@mojaloop/central-services-logger')
|
|
28
|
-
const Catbox = require('@hapi/catbox')
|
|
29
|
-
const CatboxMemory = require('@hapi/catbox-memory')
|
|
30
|
-
const Http = require('./http')
|
|
31
|
-
const Enum = require('../enums')
|
|
32
|
-
const partition = 'participant-cache'
|
|
33
|
-
const clientOptions = { partition }
|
|
34
|
-
const Mustache = require('mustache')
|
|
35
|
-
const request = require('./request')
|
|
36
|
-
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
37
|
-
const Metrics = require('@mojaloop/central-services-metrics')
|
|
38
|
-
const { Map } = require('immutable')
|
|
39
|
-
|
|
40
|
-
const PARTICIPANT_CACHE_KEY = 'participantCache'
|
|
41
|
-
let client
|
|
42
|
-
let policy
|
|
43
|
-
let switchEndpoint
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @function fetchParticipants
|
|
47
|
-
*
|
|
48
|
-
* @description This populates the cache of participants
|
|
49
|
-
*
|
|
50
|
-
* @returns {object} participantMap Returns the object containing the participants
|
|
51
|
-
*/
|
|
52
|
-
const fetchParticipants = async () => {
|
|
53
|
-
const histTimer = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
54
|
-
'fetchParticipants',
|
|
55
|
-
'fetchParticipants - Metrics for fetchParticipants',
|
|
56
|
-
['success']
|
|
57
|
-
).startTimer()
|
|
58
|
-
try {
|
|
59
|
-
Logger.isDebugEnabled && Logger.debug('participantCache::fetchParticipants := Refreshing participant cache')
|
|
60
|
-
const defaultHeaders = Http.SwitchDefaultHeaders(Enum.Http.HeaderResources.SWITCH, Enum.Http.HeaderResources.PARTICIPANTS, Enum.Http.HeaderResources.SWITCH)
|
|
61
|
-
// The templates have a counter-intuitive naming structure, but this is the correct template to use
|
|
62
|
-
// for a GET /participants request.
|
|
63
|
-
const url = Mustache.render(switchEndpoint + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_POST)
|
|
64
|
-
Logger.isDebugEnabled && Logger.debug(`participantCache::fetchParticipants := URL: ${url}`)
|
|
65
|
-
const response = await request.sendRequest(url, defaultHeaders, Enum.Http.HeaderResources.SWITCH, Enum.Http.HeaderResources.SWITCH)
|
|
66
|
-
const participants = response.data
|
|
67
|
-
const participantMap = {}
|
|
68
|
-
if (Array.isArray(participants)) {
|
|
69
|
-
participants.forEach(participant => {
|
|
70
|
-
participantMap[participant.name] = participant
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
histTimer({ success: true })
|
|
74
|
-
return participantMap
|
|
75
|
-
} catch (e) {
|
|
76
|
-
histTimer({ success: false })
|
|
77
|
-
Logger.isErrorEnabled && Logger.error(`participantCache::fetchParticipants:: ERROR:'${e}'`)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @function initializeCache
|
|
83
|
-
*
|
|
84
|
-
* @description This initializes the cache for endpoints
|
|
85
|
-
* @param {object} policyOptions The Endpoint_Cache_Config for the Cache being stored https://hapi.dev/module/catbox/api/?v=12.1.1#policy
|
|
86
|
-
|
|
87
|
-
* @returns {boolean} Returns true on successful initialization of the cache, throws error on failures
|
|
88
|
-
*/
|
|
89
|
-
exports.initializeCache = async (policyOptions) => {
|
|
90
|
-
try {
|
|
91
|
-
Logger.isDebugEnabled && Logger.debug(`participantCache::initializeCache::start::clientOptions - ${JSON.stringify(clientOptions)}`)
|
|
92
|
-
client = new Catbox.Client(CatboxMemory, clientOptions)
|
|
93
|
-
await client.start()
|
|
94
|
-
policyOptions.generateFunc = fetchParticipants
|
|
95
|
-
Logger.isDebugEnabled && Logger.debug(`participantCache::initializeCache::start::policyOptions - ${JSON.stringify(policyOptions)}`)
|
|
96
|
-
policy = new Catbox.Policy(policyOptions, client, partition)
|
|
97
|
-
Logger.isDebugEnabled && Logger.debug('participantCache::initializeCache::Cache initialized successfully')
|
|
98
|
-
return true
|
|
99
|
-
} catch (err) {
|
|
100
|
-
console.log(err)
|
|
101
|
-
Logger.isErrorEnabled && Logger.error(`participantCache::Cache error:: ERROR:'${err}'`)
|
|
102
|
-
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* @function getParticipant
|
|
108
|
-
*
|
|
109
|
-
* @description It returns the participant data for a given fsp and type from the cache if the cache is still valid, otherwise it will refresh the cache and return the value
|
|
110
|
-
*
|
|
111
|
-
* @param {string} switchUrl the endpoint for the switch
|
|
112
|
-
* @param {string} fsp - the id of the fsp
|
|
113
|
-
*
|
|
114
|
-
* @returns {string} - Returns the endpoint, throws error if failure occurs
|
|
115
|
-
*/
|
|
116
|
-
exports.getParticipant = async (switchUrl, fsp) => {
|
|
117
|
-
const histTimer = !!Metrics.isInitiated() && Metrics.getHistogram(
|
|
118
|
-
'getParticipant',
|
|
119
|
-
'getParticipant - Metrics for getParticipant with cache hit rate',
|
|
120
|
-
['success', 'hit']
|
|
121
|
-
).startTimer()
|
|
122
|
-
switchEndpoint = switchUrl
|
|
123
|
-
Logger.isDebugEnabled && Logger.debug('participantCache::getParticipant')
|
|
124
|
-
let participant
|
|
125
|
-
try {
|
|
126
|
-
// If a service passes in `getDecoratedValue` as true, then an object
|
|
127
|
-
// { value, cached, report } is returned, where value is the cached value,
|
|
128
|
-
// cached is null on a cache miss.
|
|
129
|
-
const participants = await policy.get(PARTICIPANT_CACHE_KEY)
|
|
130
|
-
if ('value' in participants && 'cached' in participants) {
|
|
131
|
-
if (participants.cached === null) {
|
|
132
|
-
histTimer({ success: true, hit: false })
|
|
133
|
-
} else {
|
|
134
|
-
histTimer({ success: true, hit: true })
|
|
135
|
-
}
|
|
136
|
-
participant = new Map(participants.value).get(fsp)
|
|
137
|
-
} else {
|
|
138
|
-
participant = new Map(participants).get(fsp)
|
|
139
|
-
histTimer({ success: true, hit: false })
|
|
140
|
-
}
|
|
141
|
-
if (participant) {
|
|
142
|
-
return participant
|
|
143
|
-
} else {
|
|
144
|
-
throw ErrorHandler.Factory.createInternalServerFSPIOPError('Participant does not exist')
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
histTimer({ success: false, hit: false })
|
|
148
|
-
Logger.isErrorEnabled && Logger.error(`participantCache::getParticipant:: ERROR:'${err}'`)
|
|
149
|
-
throw ErrorHandler.Factory.reformatFSPIOPError(err)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @function stopCache
|
|
155
|
-
*
|
|
156
|
-
* @description It stops the cache client
|
|
157
|
-
*
|
|
158
|
-
* @returns {boolean} - Returns the status
|
|
159
|
-
*/
|
|
160
|
-
exports.stopCache = async () => {
|
|
161
|
-
Logger.isDebugEnabled && Logger.debug('participantCache::stopCache::Stopping the cache')
|
|
162
|
-
if (client) {
|
|
163
|
-
return client.stop()
|
|
164
|
-
}
|
|
165
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const Test = require('tapes')(require('tape'))
|
|
4
|
-
const src = '../../../src'
|
|
5
|
-
const Sinon = require('sinon')
|
|
6
|
-
const Cache = require(`${src}/util/participants`)
|
|
7
|
-
const request = require(`${src}/util/request`)
|
|
8
|
-
const Catbox = require('@hapi/catbox')
|
|
9
|
-
const Config = require('../../util/config')
|
|
10
|
-
const Http = require(`${src}/util`).Http
|
|
11
|
-
const Enum = require(`${src}`).Enum
|
|
12
|
-
const Mustache = require('mustache')
|
|
13
|
-
const Helper = require('../../util/helper')
|
|
14
|
-
const Logger = require('@mojaloop/central-services-logger')
|
|
15
|
-
const Metrics = require('@mojaloop/central-services-metrics')
|
|
16
|
-
|
|
17
|
-
Test('Participants Cache Test', participantsCacheTest => {
|
|
18
|
-
let sandbox
|
|
19
|
-
|
|
20
|
-
participantsCacheTest.beforeEach(async test => {
|
|
21
|
-
Metrics.setup({
|
|
22
|
-
INSTRUMENTATION: {
|
|
23
|
-
METRICS: {
|
|
24
|
-
DISABLED: false,
|
|
25
|
-
config: {
|
|
26
|
-
timeout: 5000,
|
|
27
|
-
prefix: 'moja_ml_',
|
|
28
|
-
defaultLabels: {
|
|
29
|
-
serviceName: 'ml-service'
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
|
-
sandbox = Sinon.createSandbox()
|
|
36
|
-
sandbox.stub(request, 'sendRequest')
|
|
37
|
-
sandbox.stub(Http, 'SwitchDefaultHeaders').returns(Helper.defaultHeaders())
|
|
38
|
-
sandbox.stub(Logger, 'isErrorEnabled').value(true)
|
|
39
|
-
sandbox.stub(Logger, 'isInfoEnabled').value(true)
|
|
40
|
-
sandbox.stub(Logger, 'isDebugEnabled').value(true)
|
|
41
|
-
test.end()
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
participantsCacheTest.afterEach(async test => {
|
|
45
|
-
sandbox.restore()
|
|
46
|
-
test.end()
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
participantsCacheTest.test('getParticipant should', async (getParticipantTest) => {
|
|
50
|
-
getParticipantTest.test('return the participant', async (test) => {
|
|
51
|
-
const fsp = 'fsp2'
|
|
52
|
-
const expectedName = 'fsp2'
|
|
53
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_POST)
|
|
54
|
-
await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
55
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve(Helper.getParticipantsResponse))
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const result = await Cache.getParticipant(Config.ENDPOINT_SOURCE_URL, fsp)
|
|
59
|
-
test.equal(result.name, expectedName, 'The results match')
|
|
60
|
-
await Cache.stopCache()
|
|
61
|
-
test.end()
|
|
62
|
-
} catch (err) {
|
|
63
|
-
test.fail('Error thrown', err)
|
|
64
|
-
test.end()
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
getParticipantTest.test('return the participant if catbox returns decoratedValue object', async (test) => {
|
|
69
|
-
const fsp = 'fsp2'
|
|
70
|
-
const expectedName = 'fsp2'
|
|
71
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_POST)
|
|
72
|
-
await Cache.initializeCache({
|
|
73
|
-
...Config.ENDPOINT_CACHE_CONFIG,
|
|
74
|
-
getDecoratedValue: true
|
|
75
|
-
})
|
|
76
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve(Helper.getParticipantsResponse))
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const result = await Cache.getParticipant(Config.ENDPOINT_SOURCE_URL, fsp)
|
|
80
|
-
test.equal(result.name, expectedName, 'The results match')
|
|
81
|
-
await Cache.stopCache()
|
|
82
|
-
test.end()
|
|
83
|
-
} catch (err) {
|
|
84
|
-
test.fail('Error thrown', err)
|
|
85
|
-
test.end()
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
getParticipantTest.test('return throw an error if array not returned in response object', async (test) => {
|
|
90
|
-
const fsp = 'fsp2'
|
|
91
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_POST)
|
|
92
|
-
await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
93
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).returns(Promise.resolve({ data: {} }))
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const participant = await Cache.getParticipant(Config.ENDPOINT_SOURCE_URL, fsp)
|
|
97
|
-
console.log(participant)
|
|
98
|
-
test.fail('should throw error')
|
|
99
|
-
await Cache.stopCache()
|
|
100
|
-
test.end()
|
|
101
|
-
} catch (err) {
|
|
102
|
-
test.ok(err instanceof Error)
|
|
103
|
-
await Cache.stopCache()
|
|
104
|
-
test.end()
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
getParticipantTest.test('throw error', async (test) => {
|
|
109
|
-
const fsp = 'fsp2'
|
|
110
|
-
const url = Mustache.render(Config.ENDPOINT_SOURCE_URL + Enum.EndPoints.FspEndpointTemplates.PARTICIPANTS_POST)
|
|
111
|
-
await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
112
|
-
request.sendRequest.withArgs(url, Helper.defaultHeaders()).throws(new Error())
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const participant = await Cache.getParticipant(Config.ENDPOINT_SOURCE_URL, fsp)
|
|
116
|
-
console.log(participant)
|
|
117
|
-
test.fail('should throw error')
|
|
118
|
-
await Cache.stopCache()
|
|
119
|
-
test.end()
|
|
120
|
-
} catch (err) {
|
|
121
|
-
test.ok(err instanceof Error)
|
|
122
|
-
await Cache.stopCache()
|
|
123
|
-
test.end()
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
await getParticipantTest.end()
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
participantsCacheTest.test('initializeCache should', async (participantsInitializeCacheTest) => {
|
|
130
|
-
participantsInitializeCacheTest.test('initializeCache cache and return true', async (test) => {
|
|
131
|
-
try {
|
|
132
|
-
const result = await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
133
|
-
test.equal(result, true, 'The results match')
|
|
134
|
-
await Cache.stopCache()
|
|
135
|
-
test.end()
|
|
136
|
-
} catch (err) {
|
|
137
|
-
test.fail('Error thrown', err)
|
|
138
|
-
test.end()
|
|
139
|
-
}
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
participantsInitializeCacheTest.test('should throw error', async (test) => {
|
|
143
|
-
try {
|
|
144
|
-
sandbox.stub(Catbox, 'Client').throws(new Error())
|
|
145
|
-
await Cache.initializeCache(Config.ENDPOINT_CACHE_CONFIG)
|
|
146
|
-
test.fail('should throw')
|
|
147
|
-
test.end()
|
|
148
|
-
} catch (err) {
|
|
149
|
-
test.ok(err instanceof Error)
|
|
150
|
-
test.end()
|
|
151
|
-
}
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
await participantsInitializeCacheTest.end()
|
|
155
|
-
})
|
|
156
|
-
participantsCacheTest.end()
|
|
157
|
-
})
|