account-lookup-service 15.6.0-snapshot.2 → 16.1.0-iso.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 +25 -0
- package/LICENSE.md +3 -4
- package/README.md +2 -0
- package/audit-ci.jsonc +2 -27
- package/config/default.json +1 -0
- package/docker/mock-proxy/README.md +21 -0
- package/docker/mock-proxy/src/config.ts +7 -0
- package/docs/Proxy/Discovery.md +20 -0
- package/docs/Proxy/FXAPI_POC_payer_conversion_RECEIVE.plantuml +577 -0
- package/docs/Proxy/FXAPI_POC_payer_conversion_SEND.plantuml +1423 -0
- package/docs/Proxy/P2P.md +11 -0
- package/docs/Proxy/Proxy pattern - Happy path.plantuml +98 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.plantuml +105 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.png +0 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.plantuml +130 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.png +0 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.plantuml +72 -0
- package/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.png +0 -0
- package/docs/Proxy/Proxy pattern - P2P.plantuml +186 -0
- package/docs/Proxy/Proxy pattern - P2P.png +0 -0
- package/docs/Proxy/Proxy pattern - Unhappy path.plantuml +103 -0
- package/docs/Proxy/Proxy pattern - Unhappy path.png +0 -0
- package/docs/Proxy/Proxy pattern - happy path.png +0 -0
- package/docs/Proxy/Readme.md +39 -0
- package/docs/Proxy/SettingUpProxys.plantuml +31 -0
- package/docs/Proxy/SettingUpProxys.png +0 -0
- package/package.json +33 -18
- package/src/constants.js +2 -1
- package/src/domain/oracle/oracle.js +63 -4
- package/src/domain/participants/participants.js +246 -129
- package/src/domain/parties/getPartiesByTypeAndID.js +28 -7
- package/src/domain/parties/parties.js +43 -14
- package/src/domain/timeout/index.js +12 -1
- package/src/handlers/index.js +4 -4
- package/src/handlers/monitoring/index.js +1 -1
- package/src/interface/api-swagger-iso20022-parties.yaml +7 -6
- package/src/interface/api-swagger.yaml +7 -6
- package/src/lib/config.js +2 -1
- package/src/lib/db.js +2 -1
- package/src/models/currency/currency.js +10 -1
- package/src/models/endpointType/endpointType.js +10 -1
- package/src/models/oracle/facade.js +42 -16
- package/src/models/oracle/oracleEndpoint.js +65 -10
- package/src/models/oracle/oracleEndpointCached.js +29 -9
- package/src/models/participantEndpoint/facade.js +40 -4
- package/src/models/partyIdType/partyIdType.js +10 -1
- package/src/plugins.js +8 -21
- package/src/server.js +11 -0
- package/test/fixtures/index.js +30 -6
- package/test/unit/api/health.test.js +3 -0
- package/test/unit/api/participants/participants.test.js +5 -7
- package/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js +0 -3
- package/test/unit/api/participants/{Type}/{ID}.test.js +0 -3
- package/test/unit/api/participants.test.js +36 -3
- package/test/unit/domain/oracle/oracle.test.js +8 -0
- package/test/unit/domain/participants/participants.test.js +83 -48
- package/test/unit/domain/parties/parties.test.js +8 -0
- package/test/unit/domain/timeout/index.test.js +8 -0
- package/test/unit/lib/config.test.js +7 -0
- package/test/unit/mocks.js +16 -11
- package/test/unit/models/oracle/oracleEndpointCached.test.js +32 -0
- package/test/unit/plugins.test.js +2 -2
- package/test/util/apiClients/BasicApiClient.js +2 -2
- package/src/lib/requestLogger.js +0 -54
- package/src/metrics/handler.js +0 -33
- package/src/metrics/plugin.js +0 -52
- package/src/metrics/routes.js +0 -43
- package/test/unit/lib/requestLogger.test.js +0 -115
@@ -87,19 +87,24 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
87
87
|
'Get party by Type and Id',
|
88
88
|
['success']
|
89
89
|
).startTimer()
|
90
|
+
const errorCounter = Metrics.getCounter('errorCount')
|
90
91
|
const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
|
91
92
|
const type = params.Type
|
92
93
|
const partySubId = params.SubId
|
94
|
+
const callbackEndpointType = utils.getPartyCbType(partySubId)
|
93
95
|
const source = headers[Headers.FSPIOP.SOURCE]
|
94
96
|
const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
|
95
|
-
|
97
|
+
let destination = headers[Headers.FSPIOP.DESTINATION]
|
98
|
+
// see https://github.com/mojaloop/design-authority/issues/79
|
99
|
+
// the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
|
100
|
+
// without consulting any oracles.
|
96
101
|
|
97
102
|
const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined
|
98
|
-
log.info('parties::getPartiesByTypeAndID::begin', { source, proxy, params })
|
103
|
+
log.info('parties::getPartiesByTypeAndID::begin', { source, destination, proxy, params })
|
99
104
|
|
100
105
|
let requester
|
101
106
|
let fspiopError
|
102
|
-
|
107
|
+
let step
|
103
108
|
try {
|
104
109
|
requester = await validateRequester({ source, proxy, proxyCache })
|
105
110
|
|
@@ -109,22 +114,22 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
109
114
|
...(partySubId && { partySubIdOrType: partySubId })
|
110
115
|
}
|
111
116
|
|
112
|
-
let destination = headers[Headers.FSPIOP.DESTINATION]
|
113
|
-
// see https://github.com/mojaloop/design-authority/issues/79
|
114
|
-
// the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
|
115
|
-
// without consulting any oracles.
|
116
117
|
if (destination) {
|
118
|
+
step = 'validateParticipant-1'
|
117
119
|
const destParticipantModel = await participant.validateParticipant(destination)
|
118
120
|
if (!destParticipantModel) {
|
121
|
+
step = 'lookupProxyByDfspId-2'
|
119
122
|
const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
120
123
|
|
121
124
|
if (!proxyId) {
|
125
|
+
log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
|
122
126
|
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
123
127
|
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
124
128
|
}
|
125
129
|
destination = proxyId
|
126
130
|
}
|
127
131
|
// all ok, go ahead and forward the request
|
132
|
+
step = 'sendRequest-3'
|
128
133
|
await participant.sendRequest(headers, destination, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
129
134
|
|
130
135
|
histTimerEnd({ success: true })
|
@@ -132,6 +137,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
132
137
|
return
|
133
138
|
}
|
134
139
|
|
140
|
+
step = 'oracleRequest-4'
|
135
141
|
const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
|
136
142
|
if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
|
137
143
|
// Oracle's API is a standard rest-style end-point Thus a GET /party on the oracle will return all participant-party records. We must filter the results based on the callbackEndpointType to make sure we remove records containing partySubIdOrType when we are in FSPIOP_CALLBACK_URL_PARTIES_GET mode:
|
@@ -159,6 +165,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
159
165
|
if (!destination) {
|
160
166
|
clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
|
161
167
|
}
|
168
|
+
step = 'validateParticipant-5'
|
162
169
|
const schemeParticipant = await participant.validateParticipant(fspId)
|
163
170
|
if (schemeParticipant) {
|
164
171
|
sentCount++
|
@@ -169,6 +176,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
169
176
|
// If the participant is not in the scheme and proxy routing is enabled,
|
170
177
|
// we should check if there is a proxy for it and send the request to the proxy
|
171
178
|
if (proxyEnabled) {
|
179
|
+
step = 'lookupProxyByDfspId-6'
|
172
180
|
const proxyName = await proxyCache.lookupProxyByDfspId(fspId)
|
173
181
|
if (!proxyName) {
|
174
182
|
log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
|
@@ -180,6 +188,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
180
188
|
}
|
181
189
|
}
|
182
190
|
})
|
191
|
+
step = 'sendRequests-7'
|
183
192
|
await Promise.all(sending)
|
184
193
|
log.info('participant.sendRequests to filtered oracle partyList are done', { sentCount })
|
185
194
|
// todo: think what if sentCount === 0 here
|
@@ -188,6 +197,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
188
197
|
let filteredProxyNames = []
|
189
198
|
|
190
199
|
if (proxyEnabled) {
|
200
|
+
step = 'getAllProxiesNames-8'
|
191
201
|
const proxyNames = await Util.proxies.getAllProxiesNames(Config.SWITCH_ENDPOINT)
|
192
202
|
filteredProxyNames = proxyNames.filter(name => name !== proxy)
|
193
203
|
}
|
@@ -203,16 +213,19 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
203
213
|
})
|
204
214
|
fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
|
205
215
|
const errorCallbackEndpointType = utils.errorPartyCbType(partySubId)
|
216
|
+
step = 'sendErrorToParticipant-9'
|
206
217
|
await participant.sendErrorToParticipant(requester, errorCallbackEndpointType,
|
207
218
|
fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan)
|
208
219
|
} else {
|
209
220
|
const alsReq = utils.alsRequestDto(source, params)
|
210
221
|
log.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec })
|
222
|
+
step = 'setSendToProxiesList-10'
|
211
223
|
const isCached = await proxyCache.setSendToProxiesList(alsReq, filteredProxyNames, proxyCacheTtlSec)
|
212
224
|
if (!isCached) {
|
213
225
|
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
|
214
226
|
}
|
215
227
|
|
228
|
+
step = 'sending-11'
|
216
229
|
const sending = filteredProxyNames.map(
|
217
230
|
proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
218
231
|
.then(({ status, data } = {}) => ({ status, data }))
|
@@ -232,6 +245,14 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
|
|
232
245
|
} catch (err) {
|
233
246
|
fspiopError = await handleErrorOnSendingCallback(err, headers, params, requester)
|
234
247
|
histTimerEnd({ success: false })
|
248
|
+
const extensions = err.extensions || []
|
249
|
+
const system = extensions.find((element) => element.key === 'system')?.value || ''
|
250
|
+
errorCounter.inc({
|
251
|
+
code: fspiopError?.apiErrorCode?.code,
|
252
|
+
system,
|
253
|
+
operation: 'getPartiesByTypeAndID',
|
254
|
+
step
|
255
|
+
})
|
235
256
|
} finally {
|
236
257
|
await utils.finishSpanWithError(childSpan, fspiopError)
|
237
258
|
}
|
@@ -38,13 +38,13 @@ const Metrics = require('@mojaloop/central-services-metrics')
|
|
38
38
|
const oracle = require('../../models/oracle/facade')
|
39
39
|
const participant = require('../../models/participantEndpoint/facade')
|
40
40
|
const { ERROR_MESSAGES } = require('../../constants')
|
41
|
-
const {
|
41
|
+
const { logger } = require('../../lib')
|
42
42
|
const Config = require('../../lib/config')
|
43
43
|
const utils = require('./utils')
|
44
44
|
const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
|
45
45
|
|
46
|
-
const
|
47
|
-
const handleErrorOnSendingCallback = utils.createErrorHandlerOnSendingCallback(Config,
|
46
|
+
const log = logger.child('domain:put-parties')
|
47
|
+
const handleErrorOnSendingCallback = utils.createErrorHandlerOnSendingCallback(Config, log)
|
48
48
|
|
49
49
|
/**
|
50
50
|
* @function putPartiesByTypeAndID
|
@@ -65,44 +65,51 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
65
65
|
'Put parties by type and id',
|
66
66
|
['success']
|
67
67
|
).startTimer()
|
68
|
+
const errorCounter = Metrics.getCounter('errorCount')
|
68
69
|
const type = params.Type
|
69
70
|
const partySubId = params.SubId
|
70
71
|
const source = headers[Headers.FSPIOP.SOURCE]
|
71
72
|
const destination = headers[Headers.FSPIOP.DESTINATION]
|
72
73
|
const proxy = headers[Headers.FSPIOP.PROXY]
|
73
74
|
const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
|
74
|
-
|
75
|
+
log.info('parties::putPartiesByTypeAndID::begin', { source, destination, proxy, params })
|
75
76
|
|
76
77
|
let sendTo
|
78
|
+
let step
|
77
79
|
try {
|
80
|
+
step = 'validateParticipant-1'
|
78
81
|
const requesterParticipant = await participant.validateParticipant(source)
|
79
82
|
if (!requesterParticipant) {
|
80
83
|
if (!proxyEnabled || !proxy) {
|
81
84
|
const errMessage = ERROR_MESSAGES.partySourceFspNotFound
|
82
85
|
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
83
86
|
}
|
87
|
+
step = 'addDfspIdToProxyMapping-1'
|
84
88
|
const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
|
85
89
|
// think,if we should throw error if isCached === false?
|
86
|
-
|
90
|
+
log.info('addDfspIdToProxyMapping is done', { source, proxy, isCached })
|
87
91
|
}
|
88
92
|
|
89
93
|
if (proxyEnabled && proxy) {
|
90
94
|
const alsReq = utils.alsRequestDto(destination, params) // or source?
|
95
|
+
step = 'receivedSuccessResponse-2'
|
91
96
|
const isExists = await proxyCache.receivedSuccessResponse(alsReq)
|
92
97
|
if (!isExists) {
|
93
|
-
|
98
|
+
log.warn('destination is NOT in scheme, and no cached sendToProxiesList', { destination, alsReq })
|
94
99
|
// think, if we need to throw an error here
|
95
100
|
} else {
|
96
101
|
const mappingPayload = {
|
97
102
|
fspId: source
|
98
103
|
}
|
104
|
+
step = 'oracleRequest-3'
|
99
105
|
await oracle.oracleRequest(headers, RestMethods.POST, params, null, mappingPayload, cache)
|
100
|
-
|
106
|
+
log.info('oracle was updated with mappingPayload', { mappingPayload, params })
|
101
107
|
}
|
102
108
|
}
|
103
|
-
|
109
|
+
step = 'validateParticipant-4'
|
104
110
|
const destinationParticipant = await participant.validateParticipant(destination)
|
105
111
|
if (!destinationParticipant) {
|
112
|
+
step = 'lookupProxyByDfspId-5'
|
106
113
|
const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
107
114
|
if (!proxyName) {
|
108
115
|
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
@@ -120,12 +127,21 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
120
127
|
partyIdentifier: params.ID,
|
121
128
|
...(partySubId && { partySubIdOrType: partySubId })
|
122
129
|
}
|
130
|
+
step = 'sendRequest-6'
|
123
131
|
await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options)
|
124
132
|
|
125
|
-
|
133
|
+
log.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo, options })
|
126
134
|
histTimerEnd({ success: true })
|
127
135
|
} catch (err) {
|
128
|
-
await handleErrorOnSendingCallback(err, headers, params, sendTo)
|
136
|
+
const fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
|
137
|
+
const extensions = err.extensions || []
|
138
|
+
const system = extensions.find((element) => element.key === 'system')?.value || ''
|
139
|
+
errorCounter.inc({
|
140
|
+
code: fspiopError?.apiErrorCode?.code,
|
141
|
+
system,
|
142
|
+
operation: 'putPartiesByTypeAndID',
|
143
|
+
step
|
144
|
+
})
|
129
145
|
histTimerEnd({ success: false })
|
130
146
|
}
|
131
147
|
}
|
@@ -149,6 +165,7 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
|
|
149
165
|
'Put parties error by type and id',
|
150
166
|
['success']
|
151
167
|
).startTimer()
|
168
|
+
const errorCounter = Metrics.getCounter('errorCount')
|
152
169
|
const partySubId = params.SubId
|
153
170
|
const destination = headers[Headers.FSPIOP.DESTINATION]
|
154
171
|
const callbackEndpointType = utils.errorPartyCbType(partySubId)
|
@@ -158,33 +175,37 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
|
|
158
175
|
|
159
176
|
let sendTo
|
160
177
|
let fspiopError
|
178
|
+
let step
|
161
179
|
|
162
180
|
try {
|
163
181
|
const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
|
164
182
|
if (proxy) {
|
165
183
|
if (isNotValidPayeeCase(payload)) {
|
166
184
|
const swappedHeaders = utils.swapSourceDestinationHeaders(headers)
|
185
|
+
step = 'oracleRequest-1'
|
167
186
|
await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache)
|
168
187
|
getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, span, cache, proxyCache)
|
169
188
|
// todo: - think if we need to send errorCallback?
|
170
189
|
// - or sentCallback after getPartiesByTypeAndID is done
|
171
|
-
|
190
|
+
log.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID:', { proxy, params, payload })
|
172
191
|
return
|
173
192
|
}
|
174
193
|
|
175
194
|
const alsReq = utils.alsRequestDto(destination, params) // or source?
|
195
|
+
step = 'receivedErrorResponse-2'
|
176
196
|
const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy)
|
177
197
|
if (!isLast) {
|
178
|
-
|
198
|
+
log.info('got NOT last error callback from proxy:', { proxy, alsReq })
|
179
199
|
return
|
180
200
|
}
|
181
201
|
}
|
182
|
-
|
202
|
+
step = 'validateParticipant-3'
|
183
203
|
const destinationParticipant = await participant.validateParticipant(destination)
|
184
204
|
|
185
205
|
if (destinationParticipant) {
|
186
206
|
sendTo = destination
|
187
207
|
} else {
|
208
|
+
step = 'lookupProxyByDfspId-4'
|
188
209
|
const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
189
210
|
if (!proxyName) {
|
190
211
|
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
@@ -195,10 +216,18 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
|
|
195
216
|
const decodedPayload = decodePayload(dataUri, { asParsed: false })
|
196
217
|
await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan)
|
197
218
|
|
198
|
-
|
219
|
+
log.info('putPartiesErrorByTypeAndID callback was sent', { sendTo })
|
199
220
|
histTimerEnd({ success: true })
|
200
221
|
} catch (err) {
|
201
222
|
fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
|
223
|
+
const extensions = err.extensions || []
|
224
|
+
const system = extensions.find((element) => element.key === 'system')?.value || ''
|
225
|
+
errorCounter.inc({
|
226
|
+
code: fspiopError?.apiErrorCode?.code,
|
227
|
+
system,
|
228
|
+
operation: 'putPartiesErrorByTypeAndID',
|
229
|
+
step
|
230
|
+
})
|
202
231
|
histTimerEnd({ success: false })
|
203
232
|
} finally {
|
204
233
|
await utils.finishSpanWithError(childSpan, fspiopError)
|
@@ -57,20 +57,31 @@ const sendTimeoutCallback = async (cacheKey) => {
|
|
57
57
|
'Egress - Interscheme parties lookup timeout callback',
|
58
58
|
['success']
|
59
59
|
).startTimer()
|
60
|
-
|
60
|
+
const errorCounter = Metrics.getCounter('errorCount')
|
61
|
+
let step
|
61
62
|
const [, destination, partyType, partyId] = cacheKey.split(':')
|
62
63
|
const { errorInformation, params, headers, endpointType, span } = await timeoutCallbackDto({ destination, partyId, partyType })
|
63
64
|
logger.debug('sendTimeoutCallback details:', { destination, partyType, partyId, cacheKey })
|
64
65
|
|
65
66
|
try {
|
67
|
+
step = 'validateParticipant-1'
|
66
68
|
await validateParticipant(destination)
|
67
69
|
await span.audit({ headers, errorInformation }, AuditEventAction.start)
|
70
|
+
step = 'sendErrorToParticipant-2'
|
68
71
|
await Participant.sendErrorToParticipant(destination, endpointType, errorInformation, headers, params, undefined, span)
|
69
72
|
histTimerEnd({ success: true })
|
70
73
|
} catch (err) {
|
71
74
|
logger.warn('error in sendTimeoutCallback: ', err)
|
72
75
|
histTimerEnd({ success: false })
|
73
76
|
const fspiopError = reformatFSPIOPError(err)
|
77
|
+
const extensions = err.extensions || []
|
78
|
+
const system = extensions.find((element) => element.key === 'system')?.value || ''
|
79
|
+
errorCounter.inc({
|
80
|
+
code: fspiopError?.apiErrorCode?.code,
|
81
|
+
system,
|
82
|
+
operation: 'sendTimeoutCallback',
|
83
|
+
step
|
84
|
+
})
|
74
85
|
await finishSpan(span, fspiopError)
|
75
86
|
throw fspiopError
|
76
87
|
}
|
package/src/handlers/index.js
CHANGED
@@ -35,7 +35,7 @@ const Package = require('../../package.json')
|
|
35
35
|
const Server = require('../server')
|
36
36
|
const { HANDLER_TYPES } = require('../constants')
|
37
37
|
const Config = require('../lib/config')
|
38
|
-
const
|
38
|
+
const log = require('../lib').logger.child('ALS-timeout-handler')
|
39
39
|
|
40
40
|
const Program = new Command()
|
41
41
|
|
@@ -51,16 +51,16 @@ Program.command('handlers')
|
|
51
51
|
const handlers = []
|
52
52
|
|
53
53
|
if (args.timeout) {
|
54
|
-
|
54
|
+
log.debug('CLI: Executing --timeout')
|
55
55
|
handlers.push(HANDLER_TYPES.TIMEOUT)
|
56
56
|
}
|
57
57
|
|
58
58
|
if (handlers.length === 0) {
|
59
|
-
|
59
|
+
log.debug('CLI: No handlers specified')
|
60
60
|
return
|
61
61
|
}
|
62
62
|
|
63
|
-
module.exports = await Server.initializeHandlers(handlers, Config,
|
63
|
+
module.exports = await Server.initializeHandlers(handlers, Config, log)
|
64
64
|
})
|
65
65
|
|
66
66
|
if (Array.isArray(process.argv) && process.argv.length > 2) {
|
@@ -18,7 +18,7 @@
|
|
18
18
|
const Hapi = require('@hapi/hapi')
|
19
19
|
const Metrics = require('@mojaloop/central-services-metrics')
|
20
20
|
const { plugin: HealthPlugin } = require('./plugins/health')
|
21
|
-
const
|
21
|
+
const MetricsPlugin = require('@mojaloop/central-services-metrics').plugin
|
22
22
|
const { logger } = require('../../lib')
|
23
23
|
|
24
24
|
let server
|
@@ -890,13 +890,14 @@ components:
|
|
890
890
|
schemas:
|
891
891
|
CorrelationId:
|
892
892
|
title: CorrelationId
|
893
|
-
pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
894
893
|
type: string
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
894
|
+
pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$|^[0-9A-HJKMNP-TV-Z]{26}$
|
895
|
+
description: >-
|
896
|
+
Identifier that correlates all messages of the same sequence.
|
897
|
+
The supported identifiers formats are for
|
898
|
+
lowercase [UUID](https://datatracker.ietf.org/doc/html/rfc9562) and
|
899
|
+
uppercase [ULID](https://github.com/ulid/spec)
|
900
|
+
example: 'b51ec534-ee48-4575-b6a9-ead2955b8069'
|
900
901
|
Currency:
|
901
902
|
title: CurrencyEnum
|
902
903
|
maxLength: 3
|
@@ -889,13 +889,14 @@ components:
|
|
889
889
|
schemas:
|
890
890
|
CorrelationId:
|
891
891
|
title: CorrelationId
|
892
|
-
pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
893
892
|
type: string
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
893
|
+
pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$|^[0-9A-HJKMNP-TV-Z]{26}$
|
894
|
+
description: >-
|
895
|
+
Identifier that correlates all messages of the same sequence.
|
896
|
+
The supported identifiers formats are for
|
897
|
+
lowercase [UUID](https://datatracker.ietf.org/doc/html/rfc9562) and
|
898
|
+
uppercase [ULID](https://github.com/ulid/spec)
|
899
|
+
example: 'b51ec534-ee48-4575-b6a9-ead2955b8069'
|
899
900
|
Currency:
|
900
901
|
title: CurrencyEnum
|
901
902
|
maxLength: 3
|
package/src/lib/config.js
CHANGED
@@ -117,6 +117,7 @@ const config = {
|
|
117
117
|
password: RC.DATABASE.PASSWORD,
|
118
118
|
database: RC.DATABASE.DATABASE
|
119
119
|
},
|
120
|
+
maxPendingAcquire: RC.DATABASE.MAX_PENDING_ACQUIRE,
|
120
121
|
pool: {
|
121
122
|
// minimum size
|
122
123
|
min: getOrDefault(RC.DATABASE.POOL_MIN_SIZE, 2),
|
@@ -167,7 +168,7 @@ const config = {
|
|
167
168
|
INSTRUMENTATION_METRICS_LABELS: RC.INSTRUMENTATION.METRICS.labels,
|
168
169
|
INSTRUMENTATION_METRICS_CONFIG: RC.INSTRUMENTATION.METRICS.config,
|
169
170
|
JWS_SIGN: RC.ENDPOINT_SECURITY.JWS.JWS_SIGN,
|
170
|
-
FSPIOP_SOURCE_TO_SIGN: RC.
|
171
|
+
FSPIOP_SOURCE_TO_SIGN: RC.HUB_PARTICIPANT.NAME,
|
171
172
|
JWS_SIGNING_KEY_PATH: RC.ENDPOINT_SECURITY.JWS.JWS_SIGNING_KEY_PATH,
|
172
173
|
API_DOC_ENDPOINTS_ENABLED: RC.API_DOC_ENDPOINTS_ENABLED || false,
|
173
174
|
FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE: RC.FEATURE_ENABLE_EXTENDED_PARTY_ID_TYPE || false,
|
package/src/lib/db.js
CHANGED
@@ -39,7 +39,16 @@ const getCurrencyById = async (currencyId) => {
|
|
39
39
|
try {
|
40
40
|
return Db.from('currency').findOne({ currencyId, isActive: true })
|
41
41
|
} catch (err) {
|
42
|
-
|
42
|
+
const extensions = [{
|
43
|
+
key: 'system',
|
44
|
+
value: '["db"]'
|
45
|
+
}]
|
46
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(
|
47
|
+
err,
|
48
|
+
undefined,
|
49
|
+
undefined,
|
50
|
+
extensions
|
51
|
+
)
|
43
52
|
}
|
44
53
|
}
|
45
54
|
|
@@ -39,7 +39,16 @@ const getEndpointTypeByType = async (type) => {
|
|
39
39
|
try {
|
40
40
|
return Db.from('endpointType').findOne({ type, isActive: true })
|
41
41
|
} catch (err) {
|
42
|
-
|
42
|
+
const extensions = [{
|
43
|
+
key: 'system',
|
44
|
+
value: '["db"]'
|
45
|
+
}]
|
46
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(
|
47
|
+
err,
|
48
|
+
undefined,
|
49
|
+
undefined,
|
50
|
+
extensions
|
51
|
+
)
|
43
52
|
}
|
44
53
|
}
|
45
54
|
|
@@ -47,10 +47,11 @@ const { hubNameRegex } = require('../../lib/util').hubNameConfig
|
|
47
47
|
* @param {object} params - uri parameters of the http request
|
48
48
|
* @param {object} query - the query parameter on the uri of the http request
|
49
49
|
* @param {object} payload - payload of the request being sent out
|
50
|
+
* @param {object} assertPendingAcquire - flag to check DB pool pending acquire limit
|
50
51
|
*
|
51
52
|
* @returns {object} returns the response from the oracle
|
52
53
|
*/
|
53
|
-
exports.oracleRequest = async (headers, method, params = {}, query = {}, payload = undefined, cache) => {
|
54
|
+
exports.oracleRequest = async (headers, method, params = {}, query = {}, payload = undefined, cache, assertPendingAcquire) => {
|
54
55
|
try {
|
55
56
|
let url
|
56
57
|
const partyIdType = params.Type
|
@@ -59,13 +60,13 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
|
|
59
60
|
const partySubIdOrType = (params && params.SubId) ? params.SubId : (query && query.partySubIdOrType) ? query.partySubIdOrType : undefined
|
60
61
|
const isGetRequest = method.toUpperCase() === Enums.Http.RestMethods.GET
|
61
62
|
if (currency && partySubIdOrType && isGetRequest) {
|
62
|
-
url = await _getOracleEndpointByTypeCurrencyAndSubId(partyIdType, partyIdentifier, currency, partySubIdOrType)
|
63
|
+
url = await _getOracleEndpointByTypeCurrencyAndSubId(partyIdType, partyIdentifier, currency, partySubIdOrType, assertPendingAcquire)
|
63
64
|
} else if (currency && isGetRequest) {
|
64
|
-
url = await _getOracleEndpointByTypeAndCurrency(partyIdType, partyIdentifier, currency)
|
65
|
+
url = await _getOracleEndpointByTypeAndCurrency(partyIdType, partyIdentifier, currency, assertPendingAcquire)
|
65
66
|
} else if (partySubIdOrType && isGetRequest) {
|
66
|
-
url = await _getOracleEndpointByTypeAndSubId(partyIdType, partyIdentifier, partySubIdOrType)
|
67
|
+
url = await _getOracleEndpointByTypeAndSubId(partyIdType, partyIdentifier, partySubIdOrType, assertPendingAcquire)
|
67
68
|
} else {
|
68
|
-
url = await _getOracleEndpointByType(partyIdType, partyIdentifier)
|
69
|
+
url = await _getOracleEndpointByType(partyIdType, partyIdentifier, assertPendingAcquire)
|
69
70
|
if (partySubIdOrType) {
|
70
71
|
payload = { ...payload, partySubIdOrType }
|
71
72
|
}
|
@@ -123,6 +124,10 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
|
|
123
124
|
throw err
|
124
125
|
}
|
125
126
|
} catch (err) {
|
127
|
+
const extensions = [{
|
128
|
+
key: 'system',
|
129
|
+
value: '["@hapi/catbox-memory","http"]'
|
130
|
+
}]
|
126
131
|
Logger.isErrorEnabled && Logger.error(`error in oracleRequest: ${err?.stack}`)
|
127
132
|
// If the error was a 400 from the Oracle, we'll modify the error to generate a response to the
|
128
133
|
// initiator of the request.
|
@@ -131,15 +136,32 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
|
|
131
136
|
err.apiErrorCode.code === ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_COMMUNICATION_ERROR.code
|
132
137
|
) {
|
133
138
|
if (err.extensions.some(ext => (ext.key === 'status' && ext.value === Enums.Http.ReturnCodes.BADREQUEST.CODE))) {
|
134
|
-
throw ErrorHandler.Factory.createFSPIOPError(
|
139
|
+
throw ErrorHandler.Factory.createFSPIOPError(
|
140
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND,
|
141
|
+
undefined,
|
142
|
+
undefined,
|
143
|
+
undefined,
|
144
|
+
extensions
|
145
|
+
)
|
135
146
|
// Added error 404 to cover a special case of the Mowali implementation
|
136
147
|
// which uses mojaloop/als-oracle-pathfinder and currently returns 404
|
137
148
|
// and in which case the Mowali implementation expects back `DESTINATION_FSP_ERROR`.
|
138
149
|
} else if (err.extensions.some(ext => (ext.key === 'status' && ext.value === Enums.Http.ReturnCodes.NOTFOUND.CODE))) {
|
139
|
-
throw ErrorHandler.Factory.createFSPIOPError(
|
150
|
+
throw ErrorHandler.Factory.createFSPIOPError(
|
151
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR,
|
152
|
+
undefined,
|
153
|
+
undefined,
|
154
|
+
undefined,
|
155
|
+
extensions
|
156
|
+
)
|
140
157
|
}
|
141
158
|
}
|
142
|
-
throw ErrorHandler.Factory.reformatFSPIOPError(
|
159
|
+
throw ErrorHandler.Factory.reformatFSPIOPError(
|
160
|
+
err,
|
161
|
+
undefined,
|
162
|
+
undefined,
|
163
|
+
extensions
|
164
|
+
)
|
143
165
|
}
|
144
166
|
}
|
145
167
|
|
@@ -151,12 +173,13 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
|
|
151
173
|
* @param {string} partyIdType - party ID type (e.g MSISDN)
|
152
174
|
* @param {string} partyIdentifier - party ID
|
153
175
|
* @param {string} currency - currency ID
|
176
|
+
* @param {object} assertPendingAcquire - flag to check DB pool pending acquire limit
|
154
177
|
*
|
155
178
|
* @returns {string} returns the endpoint to the oracle
|
156
179
|
*/
|
157
|
-
const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, currency) => {
|
180
|
+
const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier, currency, assertPendingAcquire) => {
|
158
181
|
let url
|
159
|
-
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency)
|
182
|
+
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency, assertPendingAcquire)
|
160
183
|
if (oracleEndpointModel.length > 0) {
|
161
184
|
if (oracleEndpointModel.length > 1) {
|
162
185
|
const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop()
|
@@ -186,12 +209,13 @@ const _getOracleEndpointByTypeAndCurrency = async (partyIdType, partyIdentifier,
|
|
186
209
|
*
|
187
210
|
* @param {string} partyIdType - party ID type (e.g MSISDN)
|
188
211
|
* @param {string} partyIdentifier - party ID
|
212
|
+
* @param {object} assertPendingAcquire - flag to check DB pool pending acquire limit
|
189
213
|
*
|
190
214
|
* @returns {string} returns the endpoint to the oracle
|
191
215
|
*/
|
192
|
-
const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => {
|
216
|
+
const _getOracleEndpointByType = async (partyIdType, partyIdentifier, assertPendingAcquire) => {
|
193
217
|
let url
|
194
|
-
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType)
|
218
|
+
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType, assertPendingAcquire)
|
195
219
|
if (oracleEndpointModel.length > 0) {
|
196
220
|
if (oracleEndpointModel.length > 1) {
|
197
221
|
const defaultOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop()
|
@@ -222,12 +246,13 @@ const _getOracleEndpointByType = async (partyIdType, partyIdentifier) => {
|
|
222
246
|
* @param {string} partyIdType - party ID type (e.g MSISDN)
|
223
247
|
* @param {string} partyIdentifier - party ID
|
224
248
|
* @param {string} partySubIdOrType - party subId
|
249
|
+
* @param {object} assertPendingAcquire - flag to check DB pool pending acquire limit
|
225
250
|
*
|
226
251
|
* @returns {string} returns the endpoint to the oracle
|
227
252
|
*/
|
228
|
-
const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, partySubIdOrType) => {
|
253
|
+
const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, partySubIdOrType, assertPendingAcquire) => {
|
229
254
|
let url
|
230
|
-
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType)
|
255
|
+
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByType(partyIdType, assertPendingAcquire)
|
231
256
|
if (oracleEndpointModel.length > 0) {
|
232
257
|
if (oracleEndpointModel.length > 1) {
|
233
258
|
const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop()
|
@@ -259,12 +284,13 @@ const _getOracleEndpointByTypeAndSubId = async (partyIdType, partyIdentifier, pa
|
|
259
284
|
* @param {string} partyIdentifier - party ID
|
260
285
|
* @param {string} currency - currency ID
|
261
286
|
* @param {string} partySubIdOrType - party subId
|
287
|
+
* @param {object} assertPendingAcquire - flag to check DB pool pending acquire limit
|
262
288
|
*
|
263
289
|
* @returns {string} returns the endpoint to the oracle
|
264
290
|
*/
|
265
|
-
const _getOracleEndpointByTypeCurrencyAndSubId = async (partyIdType, partyIdentifier, currency, partySubIdOrType) => {
|
291
|
+
const _getOracleEndpointByTypeCurrencyAndSubId = async (partyIdType, partyIdentifier, currency, partySubIdOrType, assertPendingAcquire) => {
|
266
292
|
let url
|
267
|
-
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency)
|
293
|
+
const oracleEndpointModel = await oracleEndpointCached.getOracleEndpointByTypeAndCurrency(partyIdType, currency, assertPendingAcquire)
|
268
294
|
if (oracleEndpointModel.length > 0) {
|
269
295
|
if (oracleEndpointModel.length > 1) {
|
270
296
|
const defautOracle = oracleEndpointModel.filter(oracle => oracle.isDefault).pop()
|