account-lookup-service 17.4.0-csi-1233.4 → 17.4.1-csi-1300.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/audit-ci.jsonc +1 -1
- package/package.json +3 -3
- package/src/domain/parties/getPartiesByTypeAndID.js +250 -174
- package/src/domain/parties/index.js +7 -4
- package/src/domain/parties/{utils.js → partiesUtils.js} +36 -16
- package/src/domain/parties/{parties.js → putParties.js} +84 -60
- package/src/domain/parties/services/GetPartiesService.js +258 -0
- package/src/domain/parties/services/index.js +32 -0
- package/src/domain/timeout/dto.js +1 -1
- package/src/lib/headers.js +2 -1
- package/src/lib/util.js +53 -2
- package/test/unit/domain/parties/parties.test.js +2 -2
- package/test/unit/domain/parties/utils.test.js +2 -2
- package/test/unit/lib/util.test.js +25 -0
package/audit-ci.jsonc
CHANGED
@@ -5,6 +5,6 @@
|
|
5
5
|
"moderate": true,
|
6
6
|
// NOTE: Please add as much information as possible to any items added to the allowList
|
7
7
|
"allowlist": [
|
8
|
-
"GHSA-
|
8
|
+
"GHSA-968p-4wvh-cqc8" // https://github.com/advisories/GHSA-968p-4wvh-cqc8
|
9
9
|
]
|
10
10
|
}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "account-lookup-service",
|
3
3
|
"description": "Account Lookup Service is used to validate Party and Participant lookups.",
|
4
|
-
"version": "17.4.
|
4
|
+
"version": "17.4.1-csi-1300.0",
|
5
5
|
"license": "Apache-2.0",
|
6
6
|
"author": "ModusBox",
|
7
7
|
"contributors": [
|
@@ -100,8 +100,8 @@
|
|
100
100
|
"@mojaloop/database-lib": "11.1.3",
|
101
101
|
"@mojaloop/event-sdk": "14.3.2",
|
102
102
|
"@mojaloop/inter-scheme-proxy-cache-lib": "2.3.7",
|
103
|
-
"@mojaloop/ml-schema-transformer-lib": "2.5.
|
104
|
-
"@mojaloop/sdk-standard-components": "19.10.
|
103
|
+
"@mojaloop/ml-schema-transformer-lib": "2.5.8",
|
104
|
+
"@mojaloop/sdk-standard-components": "19.10.3",
|
105
105
|
"@now-ims/hapi-now-auth": "2.1.0",
|
106
106
|
"ajv": "8.17.1",
|
107
107
|
"ajv-keywords": "5.1.0",
|
@@ -24,50 +24,37 @@
|
|
24
24
|
**********/
|
25
25
|
|
26
26
|
const { Enum, Util } = require('@mojaloop/central-services-shared')
|
27
|
-
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
27
|
+
// const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
28
28
|
const Metrics = require('@mojaloop/central-services-metrics')
|
29
29
|
|
30
|
-
const config = require('../../lib/config')
|
31
30
|
const oracle = require('../../models/oracle/facade')
|
32
31
|
const participant = require('../../models/participantEndpoint/facade')
|
33
|
-
const
|
34
|
-
const
|
35
|
-
const logger = require('../../lib')
|
36
|
-
const
|
37
|
-
const
|
38
|
-
const
|
39
|
-
|
40
|
-
const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints
|
41
|
-
const { Headers, RestMethods } = Enum.Http
|
42
|
-
|
43
|
-
const proxyCacheTtlSec = 40 // todo: make configurable
|
44
|
-
|
45
|
-
const validateRequester = async ({ source, proxy, proxyCache }) => {
|
46
|
-
const log = logger.child({ source, method: 'validateRequester' })
|
47
|
-
const sourceParticipant = await participant.validateParticipant(source)
|
48
|
-
if (sourceParticipant) {
|
49
|
-
log.debug('source is in scheme')
|
50
|
-
return source
|
51
|
-
}
|
32
|
+
const config = require('../../lib/config')
|
33
|
+
const libUtil = require('../../lib/util')
|
34
|
+
const { logger } = require('../../lib')
|
35
|
+
// const { createCallbackHeaders } = require('../../lib/headers')
|
36
|
+
// const { ERROR_MESSAGES } = require('../../constants')
|
37
|
+
const { GetPartiesService } = require('./services')
|
38
|
+
const partiesUtils = require('./partiesUtils')
|
52
39
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
57
|
-
}
|
40
|
+
// const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints
|
41
|
+
// const { Headers, RestMethods } = Enum.Http
|
42
|
+
const { Headers } = Enum.Http
|
58
43
|
|
59
|
-
|
60
|
-
if (!proxyParticipant) {
|
61
|
-
const errMessage = ERROR_MESSAGES.partyProxyNotFound
|
62
|
-
log.warn(errMessage, { proxy })
|
63
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
64
|
-
}
|
44
|
+
// const proxyCacheTtlSec = 40 // todo: make configurable
|
65
45
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
46
|
+
const createDeps = ({ cache, proxyCache, childSpan, log, stepState }) => Object.freeze({
|
47
|
+
cache,
|
48
|
+
proxyCache,
|
49
|
+
childSpan,
|
50
|
+
log,
|
51
|
+
stepState,
|
52
|
+
config,
|
53
|
+
oracle,
|
54
|
+
participant,
|
55
|
+
proxies: Util.proxies,
|
56
|
+
partiesUtils
|
57
|
+
})
|
71
58
|
|
72
59
|
/**
|
73
60
|
* @function getPartiesByTypeAndID
|
@@ -83,178 +70,267 @@ const validateRequester = async ({ source, proxy, proxyCache }) => {
|
|
83
70
|
* @param {IProxyCache} [proxyCache] - IProxyCache instance
|
84
71
|
*/
|
85
72
|
const getPartiesByTypeAndID = async (headers, params, method, query, span, cache, proxyCache = undefined) => {
|
73
|
+
const component = getPartiesByTypeAndID.name
|
86
74
|
const histTimerEnd = Metrics.getHistogram(
|
87
|
-
|
75
|
+
component,
|
88
76
|
'Get party by Type and Id',
|
89
77
|
['success']
|
90
78
|
).startTimer()
|
91
|
-
const log = logger.child({ params })
|
92
|
-
const
|
93
|
-
const
|
94
|
-
|
95
|
-
const
|
79
|
+
const log = logger.child({ component, params })
|
80
|
+
const stepState = libUtil.initStepState()
|
81
|
+
const childSpan = span ? span.getChild(component) : undefined
|
82
|
+
|
83
|
+
const service = new GetPartiesService(createDeps({
|
84
|
+
cache, proxyCache, childSpan, log, stepState
|
85
|
+
}))
|
86
|
+
|
87
|
+
const proxyEnabled = !!(config.PROXY_CACHE_CONFIG.enabled && proxyCache)
|
96
88
|
const source = headers[Headers.FSPIOP.SOURCE]
|
97
89
|
const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
|
98
|
-
|
90
|
+
const destination = headers[Headers.FSPIOP.DESTINATION]
|
99
91
|
// see https://github.com/mojaloop/design-authority/issues/79
|
100
92
|
// the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
|
101
93
|
// without consulting any oracles.
|
102
94
|
|
103
|
-
const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined
|
104
95
|
log.info('parties::getPartiesByTypeAndID start', { source, destination, proxy })
|
105
96
|
|
106
97
|
let requester
|
107
98
|
let fspiopError
|
108
|
-
let step
|
109
|
-
try {
|
110
|
-
requester = await validateRequester({ source, proxy, proxyCache })
|
111
99
|
|
112
|
-
|
113
|
-
|
114
|
-
partyIdentifier: params.ID,
|
115
|
-
...(partySubId && { partySubIdOrType: partySubId })
|
116
|
-
}
|
100
|
+
try {
|
101
|
+
requester = await service.validateRequester({ source, proxy })
|
117
102
|
|
118
103
|
if (destination) {
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
124
|
-
|
125
|
-
if (!proxyId) {
|
126
|
-
log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
|
127
|
-
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
128
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
129
|
-
}
|
130
|
-
destination = proxyId
|
131
|
-
}
|
132
|
-
// all ok, go ahead and forward the request
|
133
|
-
step = 'sendRequest-3'
|
134
|
-
await participant.sendRequest(headers, destination, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
135
|
-
|
104
|
+
await service.forwardRequestToDestination({ destination, headers, params })
|
105
|
+
// await forwardRequestToDestination({
|
106
|
+
// destination, headers, options, callbackEndpointType, childSpan, proxyEnabled, proxyCache, log, stepState
|
107
|
+
// })
|
136
108
|
histTimerEnd({ success: true })
|
137
|
-
log.info('discovery getPartiesByTypeAndID request was sent to destination', { destination })
|
138
109
|
return
|
139
110
|
}
|
140
111
|
|
141
|
-
|
142
|
-
const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
|
143
|
-
if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
|
144
|
-
// 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:
|
145
|
-
let filteredResponsePartyList
|
146
|
-
switch (callbackEndpointType) {
|
147
|
-
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
|
148
|
-
filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
|
149
|
-
break
|
150
|
-
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
|
151
|
-
filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === partySubId) // Filter records that match partySubIdOrType
|
152
|
-
break
|
153
|
-
default:
|
154
|
-
filteredResponsePartyList = response // Fallback to providing the standard list
|
155
|
-
}
|
156
|
-
|
157
|
-
if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) {
|
158
|
-
const errMessage = 'Requested FSP/Party not found'
|
159
|
-
log.warn(errMessage)
|
160
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
161
|
-
}
|
112
|
+
const response = await service.sendOracleDiscoveryRequest({ headers, params, query })
|
162
113
|
|
163
|
-
|
164
|
-
const
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
step = 'validateParticipant-5'
|
171
|
-
const schemeParticipant = await participant.validateParticipant(fspId)
|
172
|
-
if (schemeParticipant) {
|
173
|
-
sentCount++
|
174
|
-
log.verbose('participant is in scheme', { fspId })
|
175
|
-
return participant.sendRequest(clonedHeaders, party.fspId, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
176
|
-
}
|
177
|
-
|
178
|
-
// If the participant is not in the scheme and proxy routing is enabled,
|
179
|
-
// we should check if there is a proxy for it and send the request to the proxy
|
180
|
-
if (proxyEnabled) {
|
181
|
-
step = 'lookupProxyByDfspId-6'
|
182
|
-
const proxyName = await proxyCache.lookupProxyByDfspId(fspId)
|
183
|
-
if (!proxyName) {
|
184
|
-
log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
|
185
|
-
// todo: delete reference in oracle
|
186
|
-
} else {
|
187
|
-
sentCount++
|
188
|
-
log.verbose('participant is NOT in scheme, use proxy name', { fspId, proxyName })
|
189
|
-
return participant.sendRequest(clonedHeaders, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
190
|
-
}
|
191
|
-
}
|
192
|
-
})
|
193
|
-
step = 'sendRequests-7'
|
194
|
-
await Promise.all(sending)
|
195
|
-
log.info('participant.sendRequests to filtered oracle partyList are done', { sentCount })
|
196
|
-
// todo: think what if sentCount === 0 here
|
114
|
+
if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
|
115
|
+
const partyList = service.filterOraclePartyList({ response, params })
|
116
|
+
await service.processOraclePartyList({ partyList, headers, params, destination })
|
117
|
+
// const partyList = filterOraclePartyList({ response, params, log, stepState })
|
118
|
+
// await processOraclePartyList({
|
119
|
+
// partyList, headers, params, destination, childSpan, proxyEnabled, proxyCache, log, stepState
|
120
|
+
// })
|
197
121
|
} else {
|
198
122
|
log.info('empty partyList form oracle, getting proxies list...', { proxyEnabled, params })
|
199
|
-
|
200
|
-
|
201
|
-
if (proxyEnabled) {
|
202
|
-
step = 'getAllProxiesNames-8'
|
203
|
-
const proxyNames = await Util.proxies.getAllProxiesNames(Config.SWITCH_ENDPOINT)
|
204
|
-
filteredProxyNames = proxyNames.filter(name => name !== proxy)
|
205
|
-
}
|
123
|
+
const proxyNames = await service.getFilteredProxyList(proxy)
|
124
|
+
// getFilteredProxyList({ config, proxy, proxyEnabled, stepState })
|
206
125
|
|
207
|
-
if (!
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
endpointTemplate: partySubId
|
213
|
-
? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
|
214
|
-
: FspEndpointTemplates.PARTIES_PUT_ERROR
|
215
|
-
})
|
216
|
-
fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
|
217
|
-
const errorCallbackEndpointType = utils.errorPartyCbType(partySubId)
|
218
|
-
step = 'sendErrorToParticipant-9'
|
219
|
-
await participant.sendErrorToParticipant(requester, errorCallbackEndpointType,
|
220
|
-
fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan)
|
126
|
+
if (!proxyNames.length) {
|
127
|
+
fspiopError = await service.sendErrorCallback({ requester, headers, params })
|
128
|
+
// sendErrorCallback({
|
129
|
+
// headers, params, requester, childSpan, stepState
|
130
|
+
// })
|
221
131
|
} else {
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
if (!isCached) {
|
227
|
-
log.warn('failed to setSendToProxiesList')
|
228
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
|
229
|
-
}
|
230
|
-
|
231
|
-
step = 'sending-11'
|
232
|
-
const sending = filteredProxyNames.map(
|
233
|
-
proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
234
|
-
.then(({ status, data } = {}) => ({ status, data }))
|
235
|
-
)
|
236
|
-
const results = await Promise.allSettled(sending)
|
237
|
-
const isOk = results.some(result => result.status === 'fulfilled')
|
238
|
-
// If, at least, one request is sent to proxy, we treat the whole flow as successful.
|
239
|
-
// Failed requests should be handled by TTL expired/timeout handler
|
240
|
-
// todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
|
241
|
-
log.info('setSendToProxiesList flow is done:', { isOk, results, filteredProxyNames, alsReq })
|
242
|
-
if (!isOk) {
|
243
|
-
log.warn('no successful requests sent to proxies')
|
244
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
|
245
|
-
}
|
132
|
+
await service.triggerSendToProxiesFlow({ proxyNames, headers, params, source })
|
133
|
+
// await triggerSendToProxiesFlow({
|
134
|
+
// proxyNames, headers, params, source, childSpan, proxyCache, log, stepState
|
135
|
+
// })
|
246
136
|
}
|
247
137
|
}
|
138
|
+
log.info('parties::getPartiesByTypeAndID is done')
|
248
139
|
histTimerEnd({ success: true })
|
249
140
|
} catch (err) {
|
250
|
-
fspiopError = await
|
141
|
+
fspiopError = await partiesUtils.createErrorHandlerOnSendingCallback(config, log)(err, headers, params, requester)
|
251
142
|
histTimerEnd({ success: false })
|
252
143
|
if (fspiopError) {
|
253
|
-
|
144
|
+
libUtil.countFspiopError(fspiopError, { operation: component, step: stepState.step })
|
254
145
|
}
|
255
146
|
} finally {
|
256
|
-
await
|
147
|
+
await libUtil.finishSpanWithError(childSpan, fspiopError)
|
257
148
|
}
|
258
149
|
}
|
259
150
|
|
151
|
+
// const validateRequester = async ({ source, proxy, proxyCache, stepState }) => {
|
152
|
+
// stepState.inProgress('validateRequester-0')
|
153
|
+
// const log = logger.child({ source, method: 'validateRequester' })
|
154
|
+
// const sourceParticipant = await participant.validateParticipant(source)
|
155
|
+
// if (sourceParticipant) {
|
156
|
+
// log.debug('source is in scheme')
|
157
|
+
// return source
|
158
|
+
// }
|
159
|
+
//
|
160
|
+
// if (!proxy) {
|
161
|
+
// const errMessage = ERROR_MESSAGES.sourceFspNotFound
|
162
|
+
// log.warn(errMessage)
|
163
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
164
|
+
// }
|
165
|
+
//
|
166
|
+
// const proxyParticipant = await participant.validateParticipant(proxy)
|
167
|
+
// if (!proxyParticipant) {
|
168
|
+
// const errMessage = ERROR_MESSAGES.partyProxyNotFound
|
169
|
+
// log.warn(errMessage, { proxy })
|
170
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
171
|
+
// }
|
172
|
+
//
|
173
|
+
// const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
|
174
|
+
// // think, what if isCached !== true?
|
175
|
+
// log.info('source is added to proxyMapping cache:', { proxy, isCached })
|
176
|
+
// return proxy
|
177
|
+
// }
|
178
|
+
|
179
|
+
// const forwardRequestToDestination = async ({
|
180
|
+
// destination, headers, options, callbackEndpointType, childSpan, proxyEnabled, proxyCache, log, stepState
|
181
|
+
// }) => {
|
182
|
+
// let sendTo = destination
|
183
|
+
// stepState.inProgress('validateDestination-1')
|
184
|
+
//
|
185
|
+
// const destParticipantModel = await participant.validateParticipant(destination)
|
186
|
+
// if (!destParticipantModel) {
|
187
|
+
// stepState.inProgress('lookupProxyDestination-2')
|
188
|
+
// const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
189
|
+
//
|
190
|
+
// if (!proxyId) {
|
191
|
+
// log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
|
192
|
+
// const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
193
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
194
|
+
// }
|
195
|
+
// sendTo = proxyId
|
196
|
+
// }
|
197
|
+
// // all ok, go ahead and forward the request
|
198
|
+
// stepState.inProgress('forwardRequest-3')
|
199
|
+
// await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
200
|
+
// log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
|
201
|
+
// }
|
202
|
+
|
203
|
+
// const filterOraclePartyList = ({ response, params, log, stepState }) => {
|
204
|
+
// // 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:
|
205
|
+
// stepState.inProgress('filterOraclePartyList-5')
|
206
|
+
// const callbackEndpointType = partiesUtils.getPartyCbType(params.SubId)
|
207
|
+
// let filteredResponsePartyList
|
208
|
+
//
|
209
|
+
// switch (callbackEndpointType) {
|
210
|
+
// case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
|
211
|
+
// filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
|
212
|
+
// break
|
213
|
+
// case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
|
214
|
+
// filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
|
215
|
+
// break
|
216
|
+
// default:
|
217
|
+
// filteredResponsePartyList = response // Fallback to providing the standard list
|
218
|
+
// }
|
219
|
+
//
|
220
|
+
// if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) {
|
221
|
+
// const errMessage = 'Requested FSP/Party not found'
|
222
|
+
// log.warn(errMessage)
|
223
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
224
|
+
// }
|
225
|
+
//
|
226
|
+
// return filteredResponsePartyList
|
227
|
+
// }
|
228
|
+
|
229
|
+
// const processOraclePartyList = async ({
|
230
|
+
// partyList, headers, params, destination, childSpan, proxyEnabled, proxyCache, log, stepState
|
231
|
+
// }) => {
|
232
|
+
// const callbackEndpointType = partiesUtils.getPartyCbType(params.SubId)
|
233
|
+
// const options = partiesUtils.partiesRequestOptionsDto(params)
|
234
|
+
//
|
235
|
+
// let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process?
|
236
|
+
// const sending = partyList.map(async party => {
|
237
|
+
// const { fspId } = party
|
238
|
+
// const clonedHeaders = { ...headers }
|
239
|
+
// if (!destination) {
|
240
|
+
// clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
|
241
|
+
// }
|
242
|
+
// stepState.inProgress('validateParticipant-6')
|
243
|
+
// const schemeParticipant = await participant.validateParticipant(fspId)
|
244
|
+
// if (schemeParticipant) {
|
245
|
+
// sentCount++
|
246
|
+
// log.info('participant is in scheme', { fspId })
|
247
|
+
// return participant.sendRequest(clonedHeaders, fspId, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
248
|
+
// }
|
249
|
+
//
|
250
|
+
// // If the participant is not in the scheme and proxy routing is enabled,
|
251
|
+
// // we should check if there is a proxy for it and send the request to the proxy
|
252
|
+
// if (proxyEnabled) {
|
253
|
+
// stepState.inProgress('lookupProxyByDfspId-7')
|
254
|
+
// const proxyName = await proxyCache.lookupProxyByDfspId(fspId)
|
255
|
+
// if (!proxyName) {
|
256
|
+
// log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
|
257
|
+
// // todo: delete reference in oracle
|
258
|
+
// } else {
|
259
|
+
// sentCount++
|
260
|
+
// log.info('participant is NOT in scheme, use proxy name', { fspId, proxyName })
|
261
|
+
// return participant.sendRequest(clonedHeaders, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
262
|
+
// }
|
263
|
+
// }
|
264
|
+
// })
|
265
|
+
// await Promise.all(sending)
|
266
|
+
// log.verbose('processOraclePartyList is done', { sentCount })
|
267
|
+
// // todo: think what if sentCount === 0 here
|
268
|
+
// }
|
269
|
+
|
270
|
+
// const getFilteredProxyList = async ({ config, proxy, proxyEnabled, stepState }) => {
|
271
|
+
// stepState.inProgress('getAllProxies-8')
|
272
|
+
// if (!proxyEnabled) return []
|
273
|
+
//
|
274
|
+
// const proxyNames = await Util.proxies.getAllProxiesNames(config.SWITCH_ENDPOINT)
|
275
|
+
// return proxyNames.filter(name => name !== proxy)
|
276
|
+
// }
|
277
|
+
|
278
|
+
// const sendErrorCallback = async ({
|
279
|
+
// headers, params, requester, childSpan, stepState
|
280
|
+
// }) => {
|
281
|
+
// stepState.inProgress('sendErrorToParticipant-9')
|
282
|
+
// const callbackHeaders = createCallbackHeaders({
|
283
|
+
// requestHeaders: headers,
|
284
|
+
// partyIdType: params.Type,
|
285
|
+
// partyIdentifier: params.ID,
|
286
|
+
// endpointTemplate: params.SubId
|
287
|
+
// ? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
|
288
|
+
// : FspEndpointTemplates.PARTIES_PUT_ERROR
|
289
|
+
// })
|
290
|
+
// const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
|
291
|
+
//
|
292
|
+
// await participant.sendErrorToParticipant(
|
293
|
+
// requester,
|
294
|
+
// partiesUtils.errorPartyCbType(params.SubId),
|
295
|
+
// fspiopError.toApiErrorObject(config.ERROR_HANDLING),
|
296
|
+
// callbackHeaders,
|
297
|
+
// params,
|
298
|
+
// childSpan
|
299
|
+
// )
|
300
|
+
// return fspiopError
|
301
|
+
// }
|
302
|
+
|
303
|
+
// const triggerSendToProxiesFlow = async ({
|
304
|
+
// proxyNames, headers, params, source, childSpan, proxyCache, log, stepState
|
305
|
+
// }) => {
|
306
|
+
// stepState.inProgress('setSendToProxiesList-10')
|
307
|
+
// const alsReq = partiesUtils.alsRequestDto(source, params)
|
308
|
+
// log.info('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
|
309
|
+
//
|
310
|
+
// const isCached = await proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
|
311
|
+
// if (!isCached) {
|
312
|
+
// log.warn('failed to setSendToProxiesList')
|
313
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
|
314
|
+
// }
|
315
|
+
//
|
316
|
+
// const callbackEndpointType = partiesUtils.getPartyCbType(params.SubId)
|
317
|
+
// const options = partiesUtils.partiesRequestOptionsDto(params)
|
318
|
+
// stepState.inProgress('sendingProxyRequests-11')
|
319
|
+
// const sending = proxyNames.map(
|
320
|
+
// proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
|
321
|
+
// .then(({ status, data } = {}) => ({ status, data }))
|
322
|
+
// )
|
323
|
+
// const results = await Promise.allSettled(sending)
|
324
|
+
// const isOk = results.some(result => result.status === 'fulfilled')
|
325
|
+
// // If, at least, one request is sent to proxy, we treat the whole flow as successful.
|
326
|
+
// // Failed requests should be handled by TTL expired/timeout handler
|
327
|
+
// // todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
|
328
|
+
// log.info('triggerSendToProxiesFlow is done:', { isOk, results, proxyNames, alsReq })
|
329
|
+
// stepState.inProgress('allSent-12')
|
330
|
+
// if (!isOk) {
|
331
|
+
// log.warn('no successful requests sent to proxies')
|
332
|
+
// throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
|
333
|
+
// }
|
334
|
+
// }
|
335
|
+
|
260
336
|
module.exports = getPartiesByTypeAndID
|
@@ -25,8 +25,11 @@
|
|
25
25
|
|
26
26
|
'use strict'
|
27
27
|
|
28
|
-
const
|
28
|
+
const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
|
29
|
+
const { putPartiesByTypeAndID, putPartiesErrorByTypeAndID } = require('./putParties')
|
29
30
|
|
30
|
-
exports
|
31
|
-
|
32
|
-
|
31
|
+
module.exports = {
|
32
|
+
getPartiesByTypeAndID,
|
33
|
+
putPartiesByTypeAndID,
|
34
|
+
putPartiesErrorByTypeAndID
|
35
|
+
}
|
@@ -1,5 +1,31 @@
|
|
1
|
+
/*****
|
2
|
+
License
|
3
|
+
--------------
|
4
|
+
Copyright © 2020-2025 Mojaloop Foundation
|
5
|
+
The Mojaloop files are made available by the Mojaloop 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
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
Contributors
|
12
|
+
--------------
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
15
|
+
should be listed with a '*' in the first column. People who have
|
16
|
+
contributed from an organization can be listed under the organization
|
17
|
+
that actually holds the copyright for their contributions (see the
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
20
|
+
optionally within square brackets <email>.
|
21
|
+
|
22
|
+
* Mojaloop Foundation
|
23
|
+
* Eugen Klymniuk <eugen.klymniuk@infitx.com>
|
24
|
+
|
25
|
+
--------------
|
26
|
+
******/
|
27
|
+
|
1
28
|
const { Enum, Util: { Hapi } } = require('@mojaloop/central-services-shared')
|
2
|
-
const EventSdk = require('@mojaloop/event-sdk')
|
3
29
|
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
4
30
|
|
5
31
|
const participant = require('../../models/participantEndpoint/facade')
|
@@ -20,18 +46,6 @@ const errorPartyCbType = (partySubId) => partySubId
|
|
20
46
|
? FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_PUT_ERROR
|
21
47
|
: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR
|
22
48
|
|
23
|
-
const finishSpanWithError = async (childSpan, fspiopError) => {
|
24
|
-
if (childSpan && !childSpan.isFinished) {
|
25
|
-
if (fspiopError) {
|
26
|
-
const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message)
|
27
|
-
await childSpan.error(fspiopError, state)
|
28
|
-
await childSpan.finish(fspiopError.message, state)
|
29
|
-
} else {
|
30
|
-
await childSpan.finish()
|
31
|
-
}
|
32
|
-
}
|
33
|
-
}
|
34
|
-
|
35
49
|
const makePutPartiesErrorPayload = async (config, fspiopError, headers, params) => {
|
36
50
|
const body = fspiopError.toApiErrorObject(config.ERROR_HANDLING)
|
37
51
|
return config.API_TYPE === Hapi.API_TYPES.iso20022
|
@@ -45,6 +59,12 @@ const alsRequestDto = (sourceId, params) => ({
|
|
45
59
|
partyId: params.ID
|
46
60
|
})
|
47
61
|
|
62
|
+
const partiesRequestOptionsDto = (params) => ({
|
63
|
+
partyIdType: params.Type,
|
64
|
+
partyIdentifier: params.ID,
|
65
|
+
...(params.SubId && { partySubIdOrType: params.SubId })
|
66
|
+
})
|
67
|
+
|
48
68
|
const swapSourceDestinationHeaders = (headers) => {
|
49
69
|
const {
|
50
70
|
[Headers.FSPIOP.SOURCE]: source,
|
@@ -62,7 +82,7 @@ const swapSourceDestinationHeaders = (headers) => {
|
|
62
82
|
// change signature to accept object
|
63
83
|
const createErrorHandlerOnSendingCallback = (config, logger) => async (err, headers, params, requester) => {
|
64
84
|
try {
|
65
|
-
logger.error('error in sending parties callback', err)
|
85
|
+
logger.error('error in sending parties callback: ', err)
|
66
86
|
const sendTo = requester || headers[Headers.FSPIOP.SOURCE]
|
67
87
|
const errorCallbackEndpointType = errorPartyCbType(params.SubId)
|
68
88
|
const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
|
@@ -75,7 +95,7 @@ const createErrorHandlerOnSendingCallback = (config, logger) => async (err, head
|
|
75
95
|
} catch (exc) {
|
76
96
|
// We can't do anything else here- we _must_ handle all errors _within_ this function because
|
77
97
|
// we've already sent a sync response- we cannot throw.
|
78
|
-
logger.error('failed to handleErrorOnSendingCallback. No further processing!', exc)
|
98
|
+
logger.error('failed to handleErrorOnSendingCallback. No further processing! ', exc)
|
79
99
|
}
|
80
100
|
}
|
81
101
|
|
@@ -84,8 +104,8 @@ module.exports = {
|
|
84
104
|
putPartyCbType,
|
85
105
|
errorPartyCbType,
|
86
106
|
makePutPartiesErrorPayload,
|
87
|
-
finishSpanWithError,
|
88
107
|
createErrorHandlerOnSendingCallback,
|
89
108
|
alsRequestDto,
|
109
|
+
partiesRequestOptionsDto,
|
90
110
|
swapSourceDestinationHeaders
|
91
111
|
}
|
@@ -37,12 +37,12 @@ const Metrics = require('@mojaloop/central-services-metrics')
|
|
37
37
|
|
38
38
|
const oracle = require('../../models/oracle/facade')
|
39
39
|
const participant = require('../../models/participantEndpoint/facade')
|
40
|
+
const libUtil = require('../../lib/util')
|
41
|
+
const Config = require('../../lib/config')
|
42
|
+
const { logger } = require('../../lib')
|
40
43
|
const { ERROR_MESSAGES } = require('../../constants')
|
41
|
-
const logger = require('../../lib').logger.child({ component: 'domain.putParties' })
|
42
44
|
|
43
|
-
const
|
44
|
-
const utils = require('./utils')
|
45
|
-
const util = require('../../lib/util')
|
45
|
+
const partiesUtils = require('./partiesUtils')
|
46
46
|
const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
|
47
47
|
|
48
48
|
/**
|
@@ -59,14 +59,13 @@ const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
|
|
59
59
|
* @param {IProxyCache} [proxyCache] - IProxyCache instance
|
60
60
|
*/
|
61
61
|
const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri, cache, proxyCache = undefined) => {
|
62
|
+
const components = putPartiesByTypeAndID.name
|
62
63
|
const histTimerEnd = Metrics.getHistogram(
|
63
|
-
|
64
|
+
components,
|
64
65
|
'Put parties by type and id',
|
65
66
|
['success']
|
66
67
|
).startTimer()
|
67
|
-
const log = logger.child({ params,
|
68
|
-
const type = params.Type
|
69
|
-
const partySubId = params.SubId
|
68
|
+
const log = logger.child({ params, components })
|
70
69
|
const source = headers[Headers.FSPIOP.SOURCE]
|
71
70
|
const destination = headers[Headers.FSPIOP.DESTINATION]
|
72
71
|
const proxy = headers[Headers.FSPIOP.PROXY]
|
@@ -75,6 +74,7 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
75
74
|
|
76
75
|
let sendTo
|
77
76
|
let step
|
77
|
+
|
78
78
|
try {
|
79
79
|
step = 'validateParticipant-1'
|
80
80
|
const requesterParticipant = await participant.validateParticipant(source)
|
@@ -91,7 +91,7 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
91
91
|
}
|
92
92
|
|
93
93
|
if (proxyEnabled && proxy) {
|
94
|
-
const alsReq =
|
94
|
+
const alsReq = partiesUtils.alsRequestDto(destination, params)
|
95
95
|
step = 'receivedSuccessResponse-2'
|
96
96
|
const isExists = await proxyCache.receivedSuccessResponse(alsReq)
|
97
97
|
if (!isExists) {
|
@@ -122,21 +122,17 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
122
122
|
}
|
123
123
|
|
124
124
|
const decodedPayload = decodePayload(dataUri, { asParsed: false })
|
125
|
-
const callbackEndpointType =
|
126
|
-
const options =
|
127
|
-
partyIdType: type,
|
128
|
-
partyIdentifier: params.ID,
|
129
|
-
...(partySubId && { partySubIdOrType: partySubId })
|
130
|
-
}
|
125
|
+
const callbackEndpointType = partiesUtils.putPartyCbType(params.SubId)
|
126
|
+
const options = partiesUtils.partiesRequestOptionsDto(params)
|
131
127
|
step = 'sendRequest-6'
|
132
128
|
await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options)
|
133
129
|
|
134
130
|
log.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo })
|
135
131
|
histTimerEnd({ success: true })
|
136
132
|
} catch (err) {
|
137
|
-
const fspiopError = await
|
133
|
+
const fspiopError = await partiesUtils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
|
138
134
|
if (fspiopError) {
|
139
|
-
|
135
|
+
libUtil.countFspiopError(fspiopError, { operation: components, step })
|
140
136
|
}
|
141
137
|
histTimerEnd({ success: false })
|
142
138
|
}
|
@@ -156,82 +152,110 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
|
|
156
152
|
* @param {IProxyCache} [proxyCache] - IProxyCache instance
|
157
153
|
*/
|
158
154
|
const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, span, cache, proxyCache = undefined) => {
|
155
|
+
const component = putPartiesErrorByTypeAndID.name
|
159
156
|
const histTimerEnd = Metrics.getHistogram(
|
160
|
-
|
157
|
+
component,
|
161
158
|
'Put parties error by type and id',
|
162
159
|
['success']
|
163
160
|
).startTimer()
|
164
|
-
const log = logger.child({ params,
|
165
|
-
const partySubId = params.SubId
|
161
|
+
const log = logger.child({ params, component })
|
166
162
|
const destination = headers[Headers.FSPIOP.DESTINATION]
|
167
|
-
const callbackEndpointType = utils.errorPartyCbType(partySubId)
|
168
163
|
const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
|
164
|
+
const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
|
169
165
|
|
170
|
-
const childSpan = span ? span.getChild(
|
166
|
+
const childSpan = span ? span.getChild(component) : undefined
|
167
|
+
const stepState = libUtil.initStepState()
|
168
|
+
log.info('parties::putPartiesErrorByTypeAndID start', { destination, proxy })
|
171
169
|
|
172
170
|
let sendTo
|
173
171
|
let fspiopError
|
174
|
-
let step
|
175
172
|
|
176
173
|
try {
|
177
|
-
const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
|
178
174
|
if (proxy) {
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
// - or sentCallback after getPartiesByTypeAndID is done
|
186
|
-
log.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID:', { proxy, params, payload })
|
187
|
-
return
|
188
|
-
}
|
189
|
-
|
190
|
-
const alsReq = utils.alsRequestDto(destination, params) // or source?
|
191
|
-
step = 'receivedErrorResponse-2'
|
192
|
-
const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy)
|
193
|
-
if (!isLast) {
|
194
|
-
log.info('got NOT last error callback from proxy:', { proxy, alsReq })
|
175
|
+
const isDone = await processProxyErrorCallback({
|
176
|
+
headers, params, payload, childSpan, cache, proxyCache, log, proxy, destination, stepState
|
177
|
+
})
|
178
|
+
if (isDone) {
|
179
|
+
log.info('putPartiesErrorByTypeAndID proxy callback was processed', { proxy })
|
180
|
+
histTimerEnd({ success: true })
|
195
181
|
return
|
196
182
|
}
|
197
183
|
}
|
198
|
-
step = 'validateParticipant-3'
|
199
|
-
const destinationParticipant = await participant.validateParticipant(destination)
|
200
184
|
|
201
|
-
|
202
|
-
|
203
|
-
}
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
log.warn(errMessage)
|
209
|
-
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage)
|
210
|
-
}
|
211
|
-
sendTo = proxyName
|
212
|
-
}
|
213
|
-
const decodedPayload = decodePayload(dataUri, { asParsed: false })
|
214
|
-
await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan)
|
185
|
+
sendTo = await identifyDestinationForErrorCallback({
|
186
|
+
destination, proxyCache, proxyEnabled, log, stepState
|
187
|
+
})
|
188
|
+
|
189
|
+
await sendErrorCallbackToParticipant({
|
190
|
+
sendTo, headers, params, dataUri, childSpan, stepState
|
191
|
+
})
|
215
192
|
|
216
193
|
log.info('putPartiesErrorByTypeAndID callback was sent', { sendTo })
|
217
194
|
histTimerEnd({ success: true })
|
218
195
|
} catch (err) {
|
219
|
-
fspiopError = await
|
196
|
+
fspiopError = await partiesUtils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
|
220
197
|
if (fspiopError) {
|
221
|
-
|
198
|
+
libUtil.countFspiopError(fspiopError, { operation: component, step: stepState.step })
|
222
199
|
}
|
223
200
|
histTimerEnd({ success: false })
|
224
201
|
} finally {
|
225
|
-
await
|
202
|
+
await libUtil.finishSpanWithError(childSpan, fspiopError)
|
226
203
|
}
|
227
204
|
}
|
228
205
|
|
206
|
+
const processProxyErrorCallback = async ({
|
207
|
+
headers, params, payload, childSpan, cache, proxyCache, log, proxy, destination, stepState
|
208
|
+
}) => {
|
209
|
+
log.verbose('processProxyErrorCallback...')
|
210
|
+
let isDone // whether or not to continue putPartiesErrorByTypeAndID
|
211
|
+
|
212
|
+
if (isNotValidPayeeCase(payload)) {
|
213
|
+
stepState.inProgress('notValidPayeeCase-1')
|
214
|
+
log.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID', { proxy, payload })
|
215
|
+
const swappedHeaders = partiesUtils.swapSourceDestinationHeaders(headers)
|
216
|
+
await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache)
|
217
|
+
getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, childSpan, cache, proxyCache)
|
218
|
+
isDone = true
|
219
|
+
} else {
|
220
|
+
stepState.inProgress('receivedErrorResponse-2')
|
221
|
+
const alsReq = partiesUtils.alsRequestDto(destination, params) // or source?
|
222
|
+
const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy)
|
223
|
+
log.info(`got${isLast ? '' : 'NOT'} last error callback from proxy`, { proxy, alsReq, isLast })
|
224
|
+
isDone = !isLast
|
225
|
+
}
|
226
|
+
return isDone
|
227
|
+
}
|
228
|
+
|
229
|
+
const identifyDestinationForErrorCallback = async ({
|
230
|
+
destination, proxyCache, proxyEnabled, log, stepState
|
231
|
+
}) => {
|
232
|
+
stepState.inProgress('validateParticipant-3')
|
233
|
+
const destinationParticipant = await participant.validateParticipant(destination)
|
234
|
+
if (destinationParticipant) return destination
|
235
|
+
|
236
|
+
stepState.inProgress('lookupProxyDestination-4')
|
237
|
+
const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
|
238
|
+
if (proxyName) return proxyName
|
239
|
+
|
240
|
+
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
241
|
+
log.warn(errMessage)
|
242
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR, errMessage)
|
243
|
+
}
|
244
|
+
|
245
|
+
const sendErrorCallbackToParticipant = async ({
|
246
|
+
sendTo, headers, params, dataUri, childSpan, stepState
|
247
|
+
}) => {
|
248
|
+
stepState.inProgress('sendErrorToParticipant-5')
|
249
|
+
const decodedPayload = decodePayload(dataUri, { asParsed: false })
|
250
|
+
const callbackEndpointType = partiesUtils.errorPartyCbType(params.SubId)
|
251
|
+
await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan)
|
252
|
+
}
|
253
|
+
|
229
254
|
function isNotValidPayeeCase (payload) {
|
230
255
|
return payload?.errorInformation?.errorCode === MojaloopApiErrorCodes.PAYEE_IDENTIFIER_NOT_VALID.code
|
231
256
|
}
|
232
257
|
|
233
258
|
module.exports = {
|
234
|
-
getPartiesByTypeAndID,
|
235
259
|
putPartiesByTypeAndID,
|
236
260
|
putPartiesErrorByTypeAndID
|
237
261
|
}
|
@@ -0,0 +1,258 @@
|
|
1
|
+
/*****
|
2
|
+
License
|
3
|
+
--------------
|
4
|
+
Copyright © 2020-2025 Mojaloop Foundation
|
5
|
+
The Mojaloop files are made available by the Mojaloop 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
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
Contributors
|
12
|
+
--------------
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
15
|
+
should be listed with a '*' in the first column. People who have
|
16
|
+
contributed from an organization can be listed under the organization
|
17
|
+
that actually holds the copyright for their contributions (see the
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
20
|
+
optionally within square brackets <email>.
|
21
|
+
|
22
|
+
* Mojaloop Foundation
|
23
|
+
* Eugen Klymniuk <eugen.klymniuk@infitx.com>
|
24
|
+
|
25
|
+
--------------
|
26
|
+
******/
|
27
|
+
|
28
|
+
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
29
|
+
const { Enum } = require('@mojaloop/central-services-shared')
|
30
|
+
const { ERROR_MESSAGES } = require('../../../constants')
|
31
|
+
const { createCallbackHeaders } = require('../../../lib/headers')
|
32
|
+
|
33
|
+
const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints
|
34
|
+
const { Headers, RestMethods } = Enum.Http
|
35
|
+
|
36
|
+
const proxyCacheTtlSec = 40 // todo: make configurable
|
37
|
+
|
38
|
+
class GetPartiesService {
|
39
|
+
#deps
|
40
|
+
|
41
|
+
constructor (deps) {
|
42
|
+
this.#deps = deps
|
43
|
+
this.log = this.#deps.log.child({ component: this.constructor.name })
|
44
|
+
this.proxyEnabled = !!(deps.config.PROXY_CACHE_CONFIG?.enabled && deps.proxyCache)
|
45
|
+
}
|
46
|
+
|
47
|
+
async handleRequest () {
|
48
|
+
// todo: add logic
|
49
|
+
}
|
50
|
+
|
51
|
+
async validateRequester ({ source, proxy }) {
|
52
|
+
this.#deps.stepState.inProgress('validateRequester-0')
|
53
|
+
const log = this.log.child({ source, method: 'validateRequester' })
|
54
|
+
|
55
|
+
const sourceParticipant = await this.#validateParticipant(source)
|
56
|
+
if (sourceParticipant) {
|
57
|
+
log.debug('source is in scheme')
|
58
|
+
return source
|
59
|
+
}
|
60
|
+
|
61
|
+
if (!proxy) {
|
62
|
+
const errMessage = ERROR_MESSAGES.sourceFspNotFound
|
63
|
+
log.warn(errMessage)
|
64
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
65
|
+
}
|
66
|
+
|
67
|
+
const proxyParticipant = await this.#validateParticipant(proxy)
|
68
|
+
if (!proxyParticipant) {
|
69
|
+
const errMessage = ERROR_MESSAGES.partyProxyNotFound
|
70
|
+
log.warn(errMessage, { proxy })
|
71
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
72
|
+
}
|
73
|
+
|
74
|
+
const isCached = await this.#deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
|
75
|
+
// think, what if isCached !== true?
|
76
|
+
log.info('source is added to proxyMapping cache:', { proxy, isCached })
|
77
|
+
return proxy
|
78
|
+
}
|
79
|
+
|
80
|
+
async forwardRequestToDestination ({ destination, headers, params }) {
|
81
|
+
this.#deps.stepState.inProgress('validateDestination-1')
|
82
|
+
const log = this.log.child({ method: 'forwardRequestToDestination' })
|
83
|
+
let sendTo = destination
|
84
|
+
|
85
|
+
const destParticipantModel = await this.#validateParticipant(destination)
|
86
|
+
if (!destParticipantModel) {
|
87
|
+
this.#deps.stepState.inProgress('lookupProxyDestination-2')
|
88
|
+
const proxyId = this.proxyEnabled && await this.#deps.proxyCache.lookupProxyByDfspId(destination)
|
89
|
+
|
90
|
+
if (!proxyId) {
|
91
|
+
log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
|
92
|
+
const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
|
93
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
94
|
+
}
|
95
|
+
sendTo = proxyId
|
96
|
+
}
|
97
|
+
// all ok, go ahead and forward the request
|
98
|
+
await this.#forwardGetPartiesRequest({ sendTo, headers, params })
|
99
|
+
log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
|
100
|
+
}
|
101
|
+
|
102
|
+
filterOraclePartyList ({ response, params }) {
|
103
|
+
// 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:
|
104
|
+
this.#deps.stepState.inProgress('filterOraclePartyList-5')
|
105
|
+
const callbackEndpointType = this.#deps.partiesUtils.getPartyCbType(params.SubId)
|
106
|
+
let filteredResponsePartyList
|
107
|
+
|
108
|
+
switch (callbackEndpointType) {
|
109
|
+
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
|
110
|
+
filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
|
111
|
+
break
|
112
|
+
case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
|
113
|
+
filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
|
114
|
+
break
|
115
|
+
default:
|
116
|
+
filteredResponsePartyList = response // Fallback to providing the standard list
|
117
|
+
}
|
118
|
+
|
119
|
+
if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) {
|
120
|
+
const errMessage = 'Requested FSP/Party not found'
|
121
|
+
this.#deps.log.warn(errMessage)
|
122
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
|
123
|
+
}
|
124
|
+
|
125
|
+
return filteredResponsePartyList
|
126
|
+
}
|
127
|
+
|
128
|
+
async processOraclePartyList ({ partyList, headers, params, destination }) {
|
129
|
+
const log = this.log.child({ method: 'processOraclePartyList' })
|
130
|
+
|
131
|
+
let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process?
|
132
|
+
const sending = partyList.map(async party => {
|
133
|
+
const { fspId } = party
|
134
|
+
const clonedHeaders = { ...headers }
|
135
|
+
if (!destination) {
|
136
|
+
clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
|
137
|
+
}
|
138
|
+
this.#deps.stepState.inProgress('validateParticipant-6')
|
139
|
+
const schemeParticipant = await this.#validateParticipant(fspId)
|
140
|
+
if (schemeParticipant) {
|
141
|
+
sentCount++
|
142
|
+
log.info('participant is in scheme', { fspId })
|
143
|
+
return this.#forwardGetPartiesRequest({
|
144
|
+
sendTo: fspId,
|
145
|
+
headers: clonedHeaders,
|
146
|
+
params
|
147
|
+
})
|
148
|
+
}
|
149
|
+
|
150
|
+
// If the participant is not in the scheme and proxy routing is enabled,
|
151
|
+
// we should check if there is a proxy for it and send the request to the proxy
|
152
|
+
if (this.proxyEnabled) {
|
153
|
+
this.#deps.stepState.inProgress('lookupProxyByDfspId-7')
|
154
|
+
const proxyName = await this.#deps.proxyCache.lookupProxyByDfspId(fspId)
|
155
|
+
if (!proxyName) {
|
156
|
+
log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
|
157
|
+
// todo: delete reference in oracle
|
158
|
+
} else {
|
159
|
+
sentCount++
|
160
|
+
log.info('participant is NOT in scheme, use proxy name', { fspId, proxyName })
|
161
|
+
return this.#forwardGetPartiesRequest({
|
162
|
+
sendTo: proxyName,
|
163
|
+
headers: clonedHeaders,
|
164
|
+
params
|
165
|
+
})
|
166
|
+
}
|
167
|
+
}
|
168
|
+
})
|
169
|
+
await Promise.all(sending)
|
170
|
+
log.verbose('processOraclePartyList is done', { sentCount })
|
171
|
+
// todo: think what if sentCount === 0 here
|
172
|
+
}
|
173
|
+
|
174
|
+
async getFilteredProxyList (proxy) {
|
175
|
+
this.#deps.stepState.inProgress('getAllProxies-8')
|
176
|
+
if (!this.proxyEnabled) return []
|
177
|
+
|
178
|
+
const proxyNames = await this.#deps.proxies.getAllProxiesNames(this.#deps.config.SWITCH_ENDPOINT)
|
179
|
+
this.log.debug('getAllProxiesNames is done', { proxyNames })
|
180
|
+
return proxyNames.filter(name => name !== proxy)
|
181
|
+
}
|
182
|
+
|
183
|
+
async triggerSendToProxiesFlow ({ proxyNames, headers, params, source }) {
|
184
|
+
const log = this.log.child({ method: 'triggerSendToProxiesFlow' })
|
185
|
+
this.#deps.stepState.inProgress('setSendToProxiesList-10')
|
186
|
+
const alsReq = this.#deps.partiesUtils.alsRequestDto(source, params)
|
187
|
+
log.info('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
|
188
|
+
|
189
|
+
const isCached = await this.#deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
|
190
|
+
if (!isCached) {
|
191
|
+
log.warn('failed to setSendToProxiesList')
|
192
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
|
193
|
+
}
|
194
|
+
|
195
|
+
this.#deps.stepState.inProgress('sendingProxyRequests-11')
|
196
|
+
const sending = proxyNames.map(
|
197
|
+
sendTo => this.#forwardGetPartiesRequest({ sendTo, headers, params })
|
198
|
+
.then(({ status, data } = {}) => ({ status, data }))
|
199
|
+
)
|
200
|
+
const results = await Promise.allSettled(sending)
|
201
|
+
const isOk = results.some(result => result.status === 'fulfilled')
|
202
|
+
// If, at least, one request is sent to proxy, we treat the whole flow as successful.
|
203
|
+
// Failed requests should be handled by TTL expired/timeout handler
|
204
|
+
// todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
|
205
|
+
log.info('triggerSendToProxiesFlow is done:', { isOk, results, proxyNames, alsReq })
|
206
|
+
this.#deps.stepState.inProgress('allSent-12')
|
207
|
+
if (!isOk) {
|
208
|
+
log.warn('no successful requests sent to proxies')
|
209
|
+
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
async #validateParticipant (participantId) {
|
214
|
+
return this.#deps.participant.validateParticipant(participantId)
|
215
|
+
}
|
216
|
+
|
217
|
+
async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
|
218
|
+
this.#deps.stepState.inProgress('forwardRequest-3')
|
219
|
+
const callbackEndpointType = this.#deps.partiesUtils.getPartyCbType(params.SubId)
|
220
|
+
const options = this.#deps.partiesUtils.partiesRequestOptionsDto(params)
|
221
|
+
|
222
|
+
return this.#deps.participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.#deps.childSpan)
|
223
|
+
}
|
224
|
+
|
225
|
+
// async sendSuccessCallback ({ headers, sendTo }) {
|
226
|
+
// return this.#deps.participant.sendRequest(headers, sendTo,)
|
227
|
+
// }
|
228
|
+
|
229
|
+
async sendErrorCallback ({ requester, headers, params }) {
|
230
|
+
this.#deps.stepState.inProgress('sendErrorCallback-9')
|
231
|
+
const callbackHeaders = createCallbackHeaders({
|
232
|
+
requestHeaders: headers,
|
233
|
+
partyIdType: params.Type,
|
234
|
+
partyIdentifier: params.ID,
|
235
|
+
endpointTemplate: params.SubId
|
236
|
+
? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
|
237
|
+
: FspEndpointTemplates.PARTIES_PUT_ERROR
|
238
|
+
})
|
239
|
+
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
|
240
|
+
|
241
|
+
await this.#deps.participant.sendErrorToParticipant(
|
242
|
+
requester,
|
243
|
+
this.#deps.partiesUtils.errorPartyCbType(params.SubId),
|
244
|
+
fspiopError.toApiErrorObject(this.#deps.config.ERROR_HANDLING),
|
245
|
+
callbackHeaders,
|
246
|
+
params,
|
247
|
+
this.#deps.childSpan
|
248
|
+
)
|
249
|
+
return fspiopError
|
250
|
+
}
|
251
|
+
|
252
|
+
async sendOracleDiscoveryRequest ({ headers, params, query }) {
|
253
|
+
this.#deps.stepState.inProgress('oracleRequest-4')
|
254
|
+
return this.#deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.#deps.cache)
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
module.exports = GetPartiesService
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/*****
|
2
|
+
License
|
3
|
+
--------------
|
4
|
+
Copyright © 2020-2025 Mojaloop Foundation
|
5
|
+
The Mojaloop files are made available by the Mojaloop 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
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
Contributors
|
12
|
+
--------------
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
15
|
+
should be listed with a '*' in the first column. People who have
|
16
|
+
contributed from an organization can be listed under the organization
|
17
|
+
that actually holds the copyright for their contributions (see the
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
20
|
+
optionally within square brackets <email>.
|
21
|
+
|
22
|
+
* Mojaloop Foundation
|
23
|
+
* Eugen Klymniuk <eugen.klymniuk@infitx.com>
|
24
|
+
|
25
|
+
--------------
|
26
|
+
******/
|
27
|
+
|
28
|
+
const GetPartiesService = require('./GetPartiesService')
|
29
|
+
|
30
|
+
module.exports = {
|
31
|
+
GetPartiesService
|
32
|
+
}
|
@@ -13,7 +13,7 @@ const EventFrameworkUtil = require('@mojaloop/central-services-shared').Util.Eve
|
|
13
13
|
|
14
14
|
const LibUtil = require('../../lib/util')
|
15
15
|
const Config = require('../../lib/config')
|
16
|
-
const partiesUtils = require('../parties/
|
16
|
+
const partiesUtils = require('../parties/partiesUtils')
|
17
17
|
|
18
18
|
const timeoutCallbackDto = async ({ destination, partyId, partyType }) => {
|
19
19
|
const headers = {
|
package/src/lib/headers.js
CHANGED
@@ -46,7 +46,8 @@ exports.createCallbackHeaders = (params) => {
|
|
46
46
|
callbackHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = params.requestHeaders[Enums.Http.Headers.FSPIOP.SOURCE]
|
47
47
|
callbackHeaders[Enums.Http.Headers.FSPIOP.HTTP_METHOD] = Enums.Http.RestMethods.PUT
|
48
48
|
callbackHeaders[Enums.Http.Headers.FSPIOP.URI] = Mustache.render(params.endpointTemplate, {
|
49
|
-
partyIdType: params.partyIdType,
|
49
|
+
partyIdType: params.partyIdType,
|
50
|
+
partyIdentifier: params.partyIdentifier
|
50
51
|
})
|
51
52
|
|
52
53
|
return callbackHeaders
|
package/src/lib/util.js
CHANGED
@@ -1,5 +1,33 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
/*****
|
2
|
+
License
|
3
|
+
--------------
|
4
|
+
Copyright © 2020-2025 Mojaloop Foundation
|
5
|
+
The Mojaloop files are made available by the Mojaloop 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
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
11
|
+
Contributors
|
12
|
+
--------------
|
13
|
+
This is the official list of the Mojaloop project contributors for this file.
|
14
|
+
Names of the original copyright holders (individuals or organizations)
|
15
|
+
should be listed with a '*' in the first column. People who have
|
16
|
+
contributed from an organization can be listed under the organization
|
17
|
+
that actually holds the copyright for their contributions (see the
|
18
|
+
Mojaloop Foundation for an example). Those individuals should have
|
19
|
+
their names indented and be marked with a '-'. Email address can be added
|
20
|
+
optionally within square brackets <email>.
|
21
|
+
|
22
|
+
* Mojaloop Foundation
|
23
|
+
* Eugen Klymniuk <eugen.klymniuk@infitx.com>
|
24
|
+
|
25
|
+
--------------
|
26
|
+
******/
|
27
|
+
|
28
|
+
const util = require('node:util')
|
29
|
+
const Path = require('node:path')
|
30
|
+
const EventSdk = require('@mojaloop/event-sdk')
|
3
31
|
const Enum = require('@mojaloop/central-services-shared').Enum
|
4
32
|
const { HeaderValidation, Hapi, rethrow } = require('@mojaloop/central-services-shared').Util
|
5
33
|
const Config = require('../lib/config')
|
@@ -70,7 +98,30 @@ const countFspiopError = (error, options) => {
|
|
70
98
|
rethrow.countFspiopError(error, options)
|
71
99
|
}
|
72
100
|
|
101
|
+
// todo: think better name
|
102
|
+
const initStepState = (initStep = 'start') => {
|
103
|
+
let step = initStep
|
104
|
+
return Object.freeze({
|
105
|
+
get step () { return step }, // or rename to current ?
|
106
|
+
inProgress (nextStep) { step = nextStep }
|
107
|
+
})
|
108
|
+
}
|
109
|
+
|
110
|
+
const finishSpanWithError = async (childSpan, fspiopError) => {
|
111
|
+
if (childSpan && !childSpan.isFinished) {
|
112
|
+
if (fspiopError) {
|
113
|
+
const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message)
|
114
|
+
await childSpan.error(fspiopError, state)
|
115
|
+
await childSpan.finish(fspiopError.message, state)
|
116
|
+
} else {
|
117
|
+
await childSpan.finish()
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
73
122
|
module.exports = {
|
123
|
+
initStepState,
|
124
|
+
finishSpanWithError,
|
74
125
|
getSpanTags,
|
75
126
|
pathForInterface,
|
76
127
|
getStackOrInspect,
|
@@ -41,8 +41,8 @@ const Metrics = require('@mojaloop/central-services-metrics')
|
|
41
41
|
|
42
42
|
const Config = require('../../../../src/lib/config')
|
43
43
|
const Db = require('../../../../src/lib/db')
|
44
|
-
const partiesDomain = require('../../../../src/domain/parties
|
45
|
-
const partiesUtils = require('../../../../src/domain/parties/
|
44
|
+
const partiesDomain = require('../../../../src/domain/parties')
|
45
|
+
const partiesUtils = require('../../../../src/domain/parties/partiesUtils')
|
46
46
|
const participant = require('../../../../src/models/participantEndpoint/facade')
|
47
47
|
const oracle = require('../../../../src/models/oracle/facade')
|
48
48
|
const oracleEndpointCached = require('../../../../src/models/oracle/oracleEndpointCached')
|
@@ -36,7 +36,7 @@ jest.mock('@mojaloop/central-services-shared', () => ({
|
|
36
36
|
|
37
37
|
const { API_TYPES } = require('@mojaloop/central-services-shared').Util.Hapi
|
38
38
|
const { logger } = require('../../../../src/lib')
|
39
|
-
const
|
39
|
+
const partiesUtils = require('../../../../src/domain/parties/partiesUtils')
|
40
40
|
const config = require('../../../../src/lib/config')
|
41
41
|
const fixtures = require('../../../fixtures')
|
42
42
|
|
@@ -48,7 +48,7 @@ describe('parties utils Tests -->', () => {
|
|
48
48
|
const headers = fixtures.partiesCallHeadersDto({ source })
|
49
49
|
const params = { ID: '1234', Type: 'MSISDN' }
|
50
50
|
|
51
|
-
const handleError =
|
51
|
+
const handleError = partiesUtils.createErrorHandlerOnSendingCallback(isoConfig, logger)
|
52
52
|
await handleError(err, headers, params, source)
|
53
53
|
|
54
54
|
expect(mockSendRequest).toHaveBeenCalledTimes(1)
|
@@ -65,4 +65,29 @@ describe('Util', () => {
|
|
65
65
|
const expectedApiExtendedAdminMockPath = path.join('interface', 'thirdparty', 'admin_swagger.json')
|
66
66
|
expect(apiExtendedAdminMockPath).toContain(expectedApiExtendedAdminMockPath)
|
67
67
|
})
|
68
|
+
|
69
|
+
describe('stepState Tests -->', () => {
|
70
|
+
it('should initiate stepState with default step', async () => {
|
71
|
+
const stepState = Util.initStepState()
|
72
|
+
expect(stepState.step).toBe('start')
|
73
|
+
})
|
74
|
+
|
75
|
+
it('should initiate stepState with custom step', async () => {
|
76
|
+
const step = 'customStep'
|
77
|
+
const stepState = Util.initStepState(step)
|
78
|
+
expect(stepState.step).toBe(step)
|
79
|
+
})
|
80
|
+
|
81
|
+
it('should set ongoing step using inProgress() method', async () => {
|
82
|
+
const step = 'nextStep'
|
83
|
+
const stepState = Util.initStepState()
|
84
|
+
stepState.inProgress(step)
|
85
|
+
expect(stepState.step).toBe(step)
|
86
|
+
})
|
87
|
+
|
88
|
+
it('should not be able to change internal state directly', async () => {
|
89
|
+
const stepState = Util.initStepState()
|
90
|
+
expect(() => { stepState.step = 'impossible' }).toThrowError()
|
91
|
+
})
|
92
|
+
})
|
68
93
|
})
|