account-lookup-service 17.4.0-csi-1233.5 → 17.4.1-csi-1300.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.0-csi-1233.5",
4
+ "version": "17.4.1-csi-1300.1",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ModusBox",
7
7
  "contributors": [
@@ -0,0 +1,49 @@
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 { proxies } = require('@mojaloop/central-services-shared').Util
29
+ const oracle = require('../../models/oracle/facade')
30
+ const participant = require('../../models/participantEndpoint/facade')
31
+ const config = require('../../lib/config')
32
+ const partiesUtils = require('./partiesUtils')
33
+
34
+ const createDeps = ({ cache, proxyCache, childSpan, log, stepState }) => Object.freeze({
35
+ cache,
36
+ proxyCache,
37
+ childSpan,
38
+ log,
39
+ stepState,
40
+ config,
41
+ oracle,
42
+ participant,
43
+ proxies,
44
+ partiesUtils
45
+ })
46
+
47
+ module.exports = {
48
+ createDeps
49
+ }
@@ -23,51 +23,11 @@
23
23
  --------------
24
24
  **********/
25
25
 
26
- const { Enum, Util } = require('@mojaloop/central-services-shared')
27
- const ErrorHandler = require('@mojaloop/central-services-error-handling')
28
26
  const Metrics = require('@mojaloop/central-services-metrics')
29
-
30
- const config = require('../../lib/config')
31
- const oracle = require('../../models/oracle/facade')
32
- const participant = require('../../models/participantEndpoint/facade')
33
- const { createCallbackHeaders } = require('../../lib/headers')
34
- const { ERROR_MESSAGES } = require('../../constants')
35
- const logger = require('../../lib').logger.child({ component: 'domain.getPartiesByTypeAndID' })
36
- const Config = require('../../lib/config')
37
- const utils = require('./utils')
38
- const util = require('../../lib/util')
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
- }
52
-
53
- if (!proxy) {
54
- const errMessage = ERROR_MESSAGES.sourceFspNotFound
55
- log.warn(errMessage)
56
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
57
- }
58
-
59
- const proxyParticipant = await participant.validateParticipant(proxy)
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
- }
65
-
66
- const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
67
- // think, what if isCached !== true?
68
- log.info('source is added to proxyMapping cache:', { proxy, isCached })
69
- return proxy
70
- }
27
+ const libUtil = require('../../lib/util')
28
+ const { logger } = require('../../lib')
29
+ const { GetPartiesService } = require('./services')
30
+ const { createDeps } = require('./deps')
71
31
 
72
32
  /**
73
33
  * @function getPartiesByTypeAndID
@@ -83,177 +43,32 @@ const validateRequester = async ({ source, proxy, proxyCache }) => {
83
43
  * @param {IProxyCache} [proxyCache] - IProxyCache instance
84
44
  */
85
45
  const getPartiesByTypeAndID = async (headers, params, method, query, span, cache, proxyCache = undefined) => {
46
+ const component = getPartiesByTypeAndID.name
86
47
  const histTimerEnd = Metrics.getHistogram(
87
- 'getPartiesByTypeAndID',
48
+ component,
88
49
  'Get party by Type and Id',
89
50
  ['success']
90
51
  ).startTimer()
91
- const log = logger.child({ params })
92
- const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
93
- const type = params.Type
94
- const partySubId = params.SubId
95
- const callbackEndpointType = utils.getPartyCbType(partySubId)
96
- const source = headers[Headers.FSPIOP.SOURCE]
97
- const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
98
- let destination = headers[Headers.FSPIOP.DESTINATION]
99
- // see https://github.com/mojaloop/design-authority/issues/79
100
- // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
101
- // without consulting any oracles.
52
+ const childSpan = span ? span.getChild(component) : undefined
53
+ const log = logger.child({ component, params })
54
+ const stepState = libUtil.initStepState()
102
55
 
103
- const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined
104
- log.info('parties::getPartiesByTypeAndID start', { source, destination, proxy })
56
+ const deps = createDeps({ cache, proxyCache, childSpan, log, stepState })
57
+ const service = new GetPartiesService(deps)
58
+ const results = {}
105
59
 
106
- let requester
107
- let fspiopError
108
- let step
109
60
  try {
110
- requester = await validateRequester({ source, proxy, proxyCache })
111
-
112
- const options = {
113
- partyIdType: type,
114
- partyIdentifier: params.ID,
115
- ...(partySubId && { partySubIdOrType: partySubId })
116
- }
117
-
118
- if (destination) {
119
- step = 'validateParticipant-1'
120
- const destParticipantModel = await participant.validateParticipant(destination)
121
- if (!destParticipantModel) {
122
- step = 'lookupProxyByDfspId-2'
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
-
136
- histTimerEnd({ success: true })
137
- log.info('discovery getPartiesByTypeAndID request was sent to destination', { destination })
138
- return
139
- }
140
-
141
- step = 'oracleRequest-4'
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
- }
162
-
163
- let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process?
164
- const sending = filteredResponsePartyList.map(async party => {
165
- const { fspId } = party
166
- const clonedHeaders = { ...headers }
167
- if (!destination) {
168
- clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
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
197
- } else {
198
- log.info('empty partyList form oracle, getting proxies list...', { proxyEnabled, params })
199
- let filteredProxyNames = []
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
- }
206
-
207
- if (!filteredProxyNames.length) {
208
- const callbackHeaders = createCallbackHeaders({
209
- requestHeaders: headers,
210
- partyIdType: type,
211
- partyIdentifier: params.ID,
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)
221
- } else {
222
- const alsReq = utils.alsRequestDto(source, params)
223
- log.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec })
224
- step = 'setSendToProxiesList-10'
225
- const isCached = await proxyCache.setSendToProxiesList(alsReq, filteredProxyNames, proxyCacheTtlSec)
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
- }
246
- }
247
- }
61
+ await service.handleRequest({ headers, params, query, results })
62
+ log.info('getPartiesByTypeAndID is done')
248
63
  histTimerEnd({ success: true })
249
64
  } catch (err) {
250
- fspiopError = await utils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, requester)
65
+ results.fspiopError = await deps.partiesUtils.createErrorHandlerOnSendingCallback(deps.config, log)(err, headers, params, results.requester)
251
66
  histTimerEnd({ success: false })
252
- if (fspiopError) {
253
- util.countFspiopError(fspiopError, { operation: 'getPartiesByTypeAndID', step })
67
+ if (results.fspiopError) {
68
+ libUtil.countFspiopError(results.fspiopError, { operation: component, step: stepState.step })
254
69
  }
255
70
  } finally {
256
- await utils.finishSpanWithError(childSpan, fspiopError)
71
+ await libUtil.finishSpanWithError(childSpan, results.fspiopError)
257
72
  }
258
73
  }
259
74
 
@@ -25,8 +25,11 @@
25
25
 
26
26
  'use strict'
27
27
 
28
- const parties = require('./parties')
28
+ const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
29
+ const { putPartiesByTypeAndID, putPartiesErrorByTypeAndID } = require('./putParties')
29
30
 
30
- exports.getPartiesByTypeAndID = parties.getPartiesByTypeAndID
31
- exports.putPartiesByTypeAndID = parties.putPartiesByTypeAndID
32
- exports.putPartiesErrorByTypeAndID = parties.putPartiesErrorByTypeAndID
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 Config = require('../../lib/config')
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
- 'putPartiesByTypeAndID',
64
+ components,
64
65
  'Put parties by type and id',
65
66
  ['success']
66
67
  ).startTimer()
67
- const log = logger.child({ params, method: 'putPartiesByTypeAndID' })
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 = utils.alsRequestDto(destination, params) // or source?
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 = utils.putPartyCbType(partySubId)
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 utils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
133
+ const fspiopError = await partiesUtils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
138
134
  if (fspiopError) {
139
- util.countFspiopError(fspiopError, { operation: 'putPartiesByTypeAndID', step })
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
- 'putPartiesErrorByTypeAndID',
157
+ component,
161
158
  'Put parties error by type and id',
162
159
  ['success']
163
160
  ).startTimer()
164
- const log = logger.child({ params, method: 'putPartiesErrorByTypeAndID' })
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('putPartiesErrorByTypeAndID') : undefined
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
- if (isNotValidPayeeCase(payload)) {
180
- const swappedHeaders = utils.swapSourceDestinationHeaders(headers)
181
- step = 'oracleRequest-1'
182
- await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache)
183
- getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, span, cache, proxyCache)
184
- // todo: - think if we need to send errorCallback?
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
- if (destinationParticipant) {
202
- sendTo = destination
203
- } else {
204
- step = 'lookupProxyByDfspId-4'
205
- const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
206
- if (!proxyName) {
207
- const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
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 utils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
196
+ fspiopError = await partiesUtils.createErrorHandlerOnSendingCallback(Config, log)(err, headers, params, sendTo)
220
197
  if (fspiopError) {
221
- util.countFspiopError(fspiopError, { operation: 'putPartiesErrorByTypeAndID', step })
198
+ libUtil.countFspiopError(fspiopError, { operation: component, step: stepState.step })
222
199
  }
223
200
  histTimerEnd({ success: false })
224
201
  } finally {
225
- await utils.finishSpanWithError(childSpan, fspiopError)
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,291 @@
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 ({ headers, params, query, results }) {
48
+ const source = headers[Headers.FSPIOP.SOURCE]
49
+ const proxy = headers[Headers.FSPIOP.PROXY]
50
+ const destination = headers[Headers.FSPIOP.DESTINATION]
51
+ // see https://github.com/mojaloop/design-authority/issues/79
52
+ // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
53
+ // without consulting any oracles.
54
+ this.log.info('handling getParties request', { source, destination, proxy })
55
+
56
+ const requester = await this.validateRequester({ source, proxy })
57
+ results.requester = requester
58
+
59
+ if (destination) {
60
+ await this.forwardRequestToDestination({ destination, headers, params })
61
+ return
62
+ }
63
+ const response = await this.sendOracleDiscoveryRequest({ headers, params, query })
64
+
65
+ if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
66
+ const partyList = this.filterOraclePartyList({ response, params })
67
+ await this.processOraclePartyList({ partyList, headers, params, destination })
68
+ return
69
+ }
70
+
71
+ this.log.info('empty partyList form oracle, getting proxyList...', { params })
72
+ const proxyNames = await this.getFilteredProxyList(proxy)
73
+
74
+ if (proxyNames.length) {
75
+ return this.triggerSendToProxiesFlow({ proxyNames, headers, params, source })
76
+ }
77
+
78
+ results.fspiopError = await this.sendErrorCallback({ requester, headers, params })
79
+ }
80
+
81
+ async validateRequester ({ source, proxy }) {
82
+ this.#deps.stepState.inProgress('validateRequester-0')
83
+ const log = this.log.child({ source, method: 'validateRequester' })
84
+
85
+ const sourceParticipant = await this.#validateParticipant(source)
86
+ if (sourceParticipant) {
87
+ log.debug('source is in scheme')
88
+ return source
89
+ }
90
+
91
+ if (!this.proxyEnabled || !proxy) {
92
+ const errMessage = ERROR_MESSAGES.sourceFspNotFound
93
+ log.warn(errMessage)
94
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
95
+ }
96
+
97
+ const proxyParticipant = await this.#validateParticipant(proxy)
98
+ if (!proxyParticipant) {
99
+ const errMessage = ERROR_MESSAGES.partyProxyNotFound
100
+ log.warn(errMessage, { proxy })
101
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
102
+ }
103
+
104
+ const isCached = await this.#deps.proxyCache.addDfspIdToProxyMapping(source, proxy)
105
+ // think, what if isCached !== true?
106
+ log.info('source is added to proxyMapping cache:', { proxy, isCached })
107
+ return proxy
108
+ }
109
+
110
+ async forwardRequestToDestination ({ destination, headers, params }) {
111
+ this.#deps.stepState.inProgress('validateDestination-1')
112
+ const log = this.log.child({ method: 'forwardRequestToDestination' })
113
+ let sendTo = destination
114
+
115
+ const destParticipantModel = await this.#validateParticipant(destination)
116
+ if (!destParticipantModel) {
117
+ this.#deps.stepState.inProgress('lookupProxyDestination-2')
118
+ const proxyId = this.proxyEnabled && await this.#deps.proxyCache.lookupProxyByDfspId(destination)
119
+
120
+ if (!proxyId) {
121
+ log.warn('no destination participant, and no dfsp-to-proxy mapping', { destination })
122
+ const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
123
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
124
+ }
125
+ sendTo = proxyId
126
+ }
127
+ // all ok, go ahead and forward the request
128
+ await this.#forwardGetPartiesRequest({ sendTo, headers, params })
129
+ log.info('discovery getPartiesByTypeAndID request was sent', { sendTo })
130
+ }
131
+
132
+ filterOraclePartyList ({ response, params }) {
133
+ // 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:
134
+ this.#deps.stepState.inProgress('filterOraclePartyList-5')
135
+ const callbackEndpointType = this.#deps.partiesUtils.getPartyCbType(params.SubId)
136
+ let filteredResponsePartyList
137
+
138
+ switch (callbackEndpointType) {
139
+ case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_GET:
140
+ filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType == null) // Filter records that DON'T contain a partySubIdOrType
141
+ break
142
+ case FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_SUB_ID_GET:
143
+ filteredResponsePartyList = response.data.partyList.filter(party => party.partySubIdOrType === params.SubId) // Filter records that match partySubIdOrType
144
+ break
145
+ default:
146
+ filteredResponsePartyList = response // Fallback to providing the standard list
147
+ }
148
+
149
+ if (!Array.isArray(filteredResponsePartyList) || !filteredResponsePartyList.length) {
150
+ const errMessage = 'Requested FSP/Party not found'
151
+ this.#deps.log.warn(errMessage)
152
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
153
+ }
154
+
155
+ return filteredResponsePartyList
156
+ }
157
+
158
+ async processOraclePartyList ({ partyList, headers, params, destination }) {
159
+ const log = this.log.child({ method: 'processOraclePartyList' })
160
+
161
+ let sentCount = 0 // if sentCount === 0 after sending, should we restart the whole process?
162
+ const sending = partyList.map(async party => {
163
+ const { fspId } = party
164
+ const clonedHeaders = { ...headers }
165
+ if (!destination) {
166
+ clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
167
+ }
168
+ this.#deps.stepState.inProgress('validateParticipant-6')
169
+ const schemeParticipant = await this.#validateParticipant(fspId)
170
+ if (schemeParticipant) {
171
+ sentCount++
172
+ log.info('participant is in scheme', { fspId })
173
+ return this.#forwardGetPartiesRequest({
174
+ sendTo: fspId,
175
+ headers: clonedHeaders,
176
+ params
177
+ })
178
+ }
179
+
180
+ // If the participant is not in the scheme and proxy routing is enabled,
181
+ // we should check if there is a proxy for it and send the request to the proxy
182
+ if (this.proxyEnabled) {
183
+ this.#deps.stepState.inProgress('lookupProxyByDfspId-7')
184
+ const proxyName = await this.#deps.proxyCache.lookupProxyByDfspId(fspId)
185
+ if (!proxyName) {
186
+ log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
187
+ // todo: delete reference in oracle
188
+ } else {
189
+ sentCount++
190
+ log.info('participant is NOT in scheme, use proxy name', { fspId, proxyName })
191
+ return this.#forwardGetPartiesRequest({
192
+ sendTo: proxyName,
193
+ headers: clonedHeaders,
194
+ params
195
+ })
196
+ }
197
+ }
198
+ })
199
+ await Promise.all(sending)
200
+ log.verbose('processOraclePartyList is done', { sentCount })
201
+ // todo: think what if sentCount === 0 here
202
+ }
203
+
204
+ async getFilteredProxyList (proxy) {
205
+ this.#deps.stepState.inProgress('getAllProxies-8')
206
+ if (!this.proxyEnabled) {
207
+ this.log.warn('proxyCache is not enabled')
208
+ return []
209
+ }
210
+
211
+ const proxyNames = await this.#deps.proxies.getAllProxiesNames(this.#deps.config.SWITCH_ENDPOINT)
212
+ this.log.debug('getAllProxiesNames is done', { proxyNames })
213
+ return proxyNames.filter(name => name !== proxy)
214
+ }
215
+
216
+ async triggerSendToProxiesFlow ({ proxyNames, headers, params, source }) {
217
+ const log = this.log.child({ method: 'triggerSendToProxiesFlow' })
218
+ this.#deps.stepState.inProgress('setSendToProxiesList-10')
219
+ const alsReq = this.#deps.partiesUtils.alsRequestDto(source, params)
220
+ log.info('starting setSendToProxiesList flow: ', { proxyNames, alsReq, proxyCacheTtlSec })
221
+
222
+ const isCached = await this.#deps.proxyCache.setSendToProxiesList(alsReq, proxyNames, proxyCacheTtlSec)
223
+ if (!isCached) {
224
+ log.warn('failed to setSendToProxiesList')
225
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
226
+ }
227
+
228
+ this.#deps.stepState.inProgress('sendingProxyRequests-11')
229
+ const sending = proxyNames.map(
230
+ sendTo => this.#forwardGetPartiesRequest({ sendTo, headers, params })
231
+ .then(({ status, data } = {}) => ({ status, data }))
232
+ )
233
+ const results = await Promise.allSettled(sending)
234
+ const isOk = results.some(result => result.status === 'fulfilled')
235
+ // If, at least, one request is sent to proxy, we treat the whole flow as successful.
236
+ // Failed requests should be handled by TTL expired/timeout handler
237
+ // todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
238
+ log.info('triggerSendToProxiesFlow is done:', { isOk, results, proxyNames, alsReq })
239
+ this.#deps.stepState.inProgress('allSent-12')
240
+ if (!isOk) {
241
+ log.warn('no successful requests sent to proxies')
242
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
243
+ }
244
+ }
245
+
246
+ async #validateParticipant (participantId) {
247
+ return this.#deps.participant.validateParticipant(participantId)
248
+ }
249
+
250
+ async #forwardGetPartiesRequest ({ sendTo, headers, params }) {
251
+ this.#deps.stepState.inProgress('forwardRequest-3')
252
+ const callbackEndpointType = this.#deps.partiesUtils.getPartyCbType(params.SubId)
253
+ const options = this.#deps.partiesUtils.partiesRequestOptionsDto(params)
254
+
255
+ return this.#deps.participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.GET, undefined, options, this.#deps.childSpan)
256
+ }
257
+
258
+ // async sendSuccessCallback ({ headers, sendTo }) {
259
+ // return this.#deps.participant.sendRequest(headers, sendTo,)
260
+ // }
261
+
262
+ async sendErrorCallback ({ requester, headers, params }) {
263
+ this.#deps.stepState.inProgress('sendErrorCallback-9')
264
+ const callbackHeaders = createCallbackHeaders({
265
+ requestHeaders: headers,
266
+ partyIdType: params.Type,
267
+ partyIdentifier: params.ID,
268
+ endpointTemplate: params.SubId
269
+ ? FspEndpointTemplates.PARTIES_SUB_ID_PUT_ERROR
270
+ : FspEndpointTemplates.PARTIES_PUT_ERROR
271
+ })
272
+ const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
273
+
274
+ await this.#deps.participant.sendErrorToParticipant(
275
+ requester,
276
+ this.#deps.partiesUtils.errorPartyCbType(params.SubId),
277
+ fspiopError.toApiErrorObject(this.#deps.config.ERROR_HANDLING),
278
+ callbackHeaders,
279
+ params,
280
+ this.#deps.childSpan
281
+ )
282
+ return fspiopError
283
+ }
284
+
285
+ async sendOracleDiscoveryRequest ({ headers, params, query }) {
286
+ this.#deps.stepState.inProgress('oracleRequest-4')
287
+ return this.#deps.oracle.oracleRequest(headers, RestMethods.GET, params, query, undefined, this.#deps.cache)
288
+ }
289
+ }
290
+
291
+ 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/utils')
16
+ const partiesUtils = require('../parties/partiesUtils')
17
17
 
18
18
  const timeoutCallbackDto = async ({ destination, partyId, partyType }) => {
19
19
  const headers = {
@@ -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, partyIdentifier: params.partyIdentifier
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
- const util = require('util')
2
- const Path = require('path')
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/parties')
45
- const partiesUtils = require('../../../../src/domain/parties/utils')
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 utils = require('../../../../src/domain/parties/utils')
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 = utils.createErrorHandlerOnSendingCallback(isoConfig, logger)
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
  })