account-lookup-service 15.6.0-iso.2 → 15.6.0-iso.21

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.
Files changed (53) hide show
  1. package/.circleci/config.yml +1 -1
  2. package/.ncurc.yaml +1 -2
  3. package/audit-ci.jsonc +3 -1
  4. package/jest.config.js +2 -0
  5. package/package.json +15 -17
  6. package/src/constants.js +2 -1
  7. package/src/domain/oracle/oracle.js +63 -4
  8. package/src/domain/participants/participants.js +259 -133
  9. package/src/domain/parties/getPartiesByTypeAndID.js +36 -15
  10. package/src/domain/parties/parties.js +44 -14
  11. package/src/domain/parties/utils.js +13 -6
  12. package/src/domain/timeout/dto.js +4 -10
  13. package/src/domain/timeout/index.js +12 -1
  14. package/src/handlers/index.js +4 -4
  15. package/src/handlers/monitoring/index.js +1 -1
  16. package/src/interface/api-swagger-iso20022-parties.yaml +7 -6
  17. package/src/interface/api-swagger.yaml +7 -6
  18. package/src/interface/fspiop-rest-v2.0-ISO20022_parties.yaml +2043 -1583
  19. package/src/lib/config.js +1 -1
  20. package/src/lib/index.js +1 -2
  21. package/src/models/currency/currency.js +10 -1
  22. package/src/models/endpointType/endpointType.js +10 -1
  23. package/src/models/oracle/facade.js +24 -3
  24. package/src/models/oracle/oracleEndpoint.js +64 -10
  25. package/src/models/oracle/oracleEndpointCached.js +22 -3
  26. package/src/models/participantEndpoint/facade.js +61 -23
  27. package/src/models/partyIdType/partyIdType.js +10 -1
  28. package/src/plugins.js +20 -9
  29. package/src/server.js +11 -19
  30. package/test/fixtures/index.js +30 -6
  31. package/test/fixtures/iso.js +1 -1
  32. package/test/unit/api/health.test.js +3 -0
  33. package/test/unit/api/participants/participants.test.js +5 -7
  34. package/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js +0 -3
  35. package/test/unit/api/participants/{Type}/{ID}.test.js +0 -3
  36. package/test/unit/api/participants.test.js +36 -3
  37. package/test/unit/domain/oracle/oracle.test.js +8 -0
  38. package/test/unit/domain/participants/participants.test.js +83 -48
  39. package/test/unit/domain/parties/parties.test.js +11 -3
  40. package/test/unit/domain/parties/utils.test.js +60 -0
  41. package/test/unit/domain/timeout/dto.test.js +1 -2
  42. package/test/unit/domain/timeout/index.test.js +8 -0
  43. package/test/unit/lib/TransformFacades.test.js +2 -1
  44. package/test/unit/lib/config.test.js +7 -0
  45. package/test/unit/models/participantEndpoint/facade.test.js +25 -8
  46. package/test/unit/plugins.test.js +4 -2
  47. package/test/util/apiClients/BasicApiClient.js +2 -2
  48. package/src/handlers/monitoring/plugins/metrics.js +0 -48
  49. package/src/lib/requestLogger.js +0 -54
  50. package/src/metrics/handler.js +0 -33
  51. package/src/metrics/plugin.js +0 -52
  52. package/src/metrics/routes.js +0 -43
  53. package/test/unit/lib/requestLogger.test.js +0 -115
@@ -32,21 +32,22 @@ const oracle = require('../../models/oracle/facade')
32
32
  const participant = require('../../models/participantEndpoint/facade')
33
33
  const { createCallbackHeaders } = require('../../lib/headers')
34
34
  const { ERROR_MESSAGES } = require('../../constants')
35
- const { loggerFactory } = require('../../lib')
35
+ const { logger } = require('../../lib')
36
36
  const Config = require('../../lib/config')
37
37
  const utils = require('./utils')
38
38
 
39
39
  const { FspEndpointTypes, FspEndpointTemplates } = Enum.EndPoints
40
40
  const { Headers, RestMethods } = Enum.Http
41
41
 
42
- const logger = loggerFactory('domain:get-parties')
42
+ const log = logger.child('domain:get-parties')
43
+ const handleErrorOnSendingCallback = utils.createErrorHandlerOnSendingCallback(Config, log)
43
44
 
44
45
  const proxyCacheTtlSec = 40 // todo: make configurable
45
46
 
46
47
  const validateRequester = async ({ source, proxy, proxyCache }) => {
47
48
  const sourceParticipant = await participant.validateParticipant(source)
48
49
  if (sourceParticipant) {
49
- logger.debug('source is in scheme', { source })
50
+ log.debug('source is in scheme', { source })
50
51
  return source
51
52
  }
52
53
 
@@ -63,7 +64,7 @@ const validateRequester = async ({ source, proxy, proxyCache }) => {
63
64
 
64
65
  const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
65
66
  // think, what if isCached !== true?
66
- logger.info('source is added to proxyMapping cache:', { source, proxy, isCached })
67
+ log.info('source is added to proxyMapping cache:', { source, proxy, isCached })
67
68
  return proxy
68
69
  }
69
70
 
@@ -86,6 +87,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
86
87
  'Get party by Type and Id',
87
88
  ['success']
88
89
  ).startTimer()
90
+ const errorCounter = Metrics.getCounter('errorCount')
89
91
  const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
90
92
  const type = params.Type
91
93
  const partySubId = params.SubId
@@ -94,11 +96,11 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
94
96
  const callbackEndpointType = utils.getPartyCbType(partySubId)
95
97
 
96
98
  const childSpan = span ? span.getChild('getPartiesByTypeAndID') : undefined
97
- logger.info('parties::getPartiesByTypeAndID::begin', { source, proxy, params })
99
+ log.info('parties::getPartiesByTypeAndID::begin', { source, proxy, params })
98
100
 
99
101
  let requester
100
102
  let fspiopError
101
-
103
+ let step
102
104
  try {
103
105
  requester = await validateRequester({ source, proxy, proxyCache })
104
106
 
@@ -113,8 +115,10 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
113
115
  // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
114
116
  // without consulting any oracles.
115
117
  if (destination) {
118
+ step = 'validateParticipant-1'
116
119
  const destParticipantModel = await participant.validateParticipant(destination)
117
120
  if (!destParticipantModel) {
121
+ step = 'lookupProxyByDfspId-2'
118
122
  const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
119
123
 
120
124
  if (!proxyId) {
@@ -124,13 +128,15 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
124
128
  destination = proxyId
125
129
  }
126
130
  // all ok, go ahead and forward the request
131
+ step = 'sendRequest-3'
127
132
  await participant.sendRequest(headers, destination, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
128
133
 
129
134
  histTimerEnd({ success: true })
130
- logger.info('discovery getPartiesByTypeAndID request was sent to destination', { destination })
135
+ log.info('discovery getPartiesByTypeAndID request was sent to destination', { destination })
131
136
  return
132
137
  }
133
138
 
139
+ step = 'oracleRequest-4'
134
140
  const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
135
141
  if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
136
142
  // 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:
@@ -158,35 +164,39 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
158
164
  if (!destination) {
159
165
  clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
160
166
  }
167
+ step = 'validateParticipant-5'
161
168
  const schemeParticipant = await participant.validateParticipant(fspId)
162
169
  if (schemeParticipant) {
163
170
  sentCount++
164
- logger.debug('participant is in scheme', { fspId })
171
+ log.debug('participant is in scheme', { fspId })
165
172
  return participant.sendRequest(clonedHeaders, party.fspId, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
166
173
  }
167
174
 
168
175
  // If the participant is not in the scheme and proxy routing is enabled,
169
176
  // we should check if there is a proxy for it and send the request to the proxy
170
177
  if (proxyEnabled) {
178
+ step = 'lookupProxyByDfspId-6'
171
179
  const proxyName = await proxyCache.lookupProxyByDfspId(fspId)
172
180
  if (!proxyName) {
173
- logger.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
181
+ log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
174
182
  // todo: delete reference in oracle
175
183
  } else {
176
184
  sentCount++
177
- logger.debug('participant NOT is in scheme, use proxy name', { fspId, proxyName })
185
+ log.debug('participant NOT is in scheme, use proxy name', { fspId, proxyName })
178
186
  return participant.sendRequest(clonedHeaders, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
179
187
  }
180
188
  }
181
189
  })
190
+ step = 'sendRequests-7'
182
191
  await Promise.all(sending)
183
- logger.info('participant.sendRequests to filtered oracle partyList are done', { sentCount })
192
+ log.info('participant.sendRequests to filtered oracle partyList are done', { sentCount })
184
193
  // todo: think what if sentCount === 0 here
185
194
  } else {
186
- logger.info('empty partyList form oracle, getting proxies list...', { proxyEnabled, params })
195
+ log.info('empty partyList form oracle, getting proxies list...', { proxyEnabled, params })
187
196
  let filteredProxyNames = []
188
197
 
189
198
  if (proxyEnabled) {
199
+ step = 'getAllProxiesNames-8'
190
200
  const proxyNames = await Util.proxies.getAllProxiesNames(Config.SWITCH_ENDPOINT)
191
201
  filteredProxyNames = proxyNames.filter(name => name !== proxy)
192
202
  }
@@ -202,16 +212,19 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
202
212
  })
203
213
  fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
204
214
  const errorCallbackEndpointType = utils.errorPartyCbType(partySubId)
215
+ step = 'sendErrorToParticipant-9'
205
216
  await participant.sendErrorToParticipant(requester, errorCallbackEndpointType,
206
217
  fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan)
207
218
  } else {
208
219
  const alsReq = utils.alsRequestDto(source, params)
209
- logger.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec })
220
+ log.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec })
221
+ step = 'setSendToProxiesList-10'
210
222
  const isCached = await proxyCache.setSendToProxiesList(alsReq, filteredProxyNames, proxyCacheTtlSec)
211
223
  if (!isCached) {
212
224
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
213
225
  }
214
226
 
227
+ step = 'sending-11'
215
228
  const sending = filteredProxyNames.map(
216
229
  proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
217
230
  .then(({ status, data } = {}) => ({ status, data }))
@@ -221,7 +234,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
221
234
  // If, at least, one request is sent to proxy, we treat the whole flow as successful.
222
235
  // Failed requests should be handled by TTL expired/timeout handler
223
236
  // todo: - think, if we should handle failed requests here (e.g., by calling receivedErrorResponse)
224
- logger.info('setSendToProxiesList flow is done:', { isOk, results, filteredProxyNames, alsReq })
237
+ log.info('setSendToProxiesList flow is done:', { isOk, results, filteredProxyNames, alsReq })
225
238
  if (!isOk) {
226
239
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.proxyConnectionError)
227
240
  }
@@ -229,8 +242,16 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
229
242
  }
230
243
  histTimerEnd({ success: true })
231
244
  } catch (err) {
232
- fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, requester)
245
+ fspiopError = await handleErrorOnSendingCallback(err, headers, params, requester)
233
246
  histTimerEnd({ success: false })
247
+ const extensions = err.extensions || []
248
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
249
+ errorCounter.inc({
250
+ code: fspiopError?.apiErrorCode,
251
+ system,
252
+ operation: 'getPartiesByTypeAndID',
253
+ step
254
+ })
234
255
  } finally {
235
256
  await utils.finishSpanWithError(childSpan, fspiopError)
236
257
  }
@@ -38,12 +38,13 @@ const Metrics = require('@mojaloop/central-services-metrics')
38
38
  const oracle = require('../../models/oracle/facade')
39
39
  const participant = require('../../models/participantEndpoint/facade')
40
40
  const { ERROR_MESSAGES } = require('../../constants')
41
- const { loggerFactory } = require('../../lib')
41
+ const { logger } = require('../../lib')
42
42
  const Config = require('../../lib/config')
43
43
  const utils = require('./utils')
44
44
  const getPartiesByTypeAndID = require('./getPartiesByTypeAndID')
45
45
 
46
- const logger = loggerFactory('domain:put-parties')
46
+ const log = logger.child('domain:put-parties')
47
+ const handleErrorOnSendingCallback = utils.createErrorHandlerOnSendingCallback(Config, log)
47
48
 
48
49
  /**
49
50
  * @function putPartiesByTypeAndID
@@ -64,44 +65,51 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
64
65
  'Put parties by type and id',
65
66
  ['success']
66
67
  ).startTimer()
68
+ const errorCounter = Metrics.getCounter('errorCount')
67
69
  const type = params.Type
68
70
  const partySubId = params.SubId
69
71
  const source = headers[Headers.FSPIOP.SOURCE]
70
72
  const destination = headers[Headers.FSPIOP.DESTINATION]
71
73
  const proxy = headers[Headers.FSPIOP.PROXY]
72
74
  const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
73
- logger.info('parties::putPartiesByTypeAndID::begin', { source, destination, proxy, params })
75
+ log.info('parties::putPartiesByTypeAndID::begin', { source, destination, proxy, params })
74
76
 
75
77
  let sendTo
78
+ let step
76
79
  try {
80
+ step = 'validateParticipant-1'
77
81
  const requesterParticipant = await participant.validateParticipant(source)
78
82
  if (!requesterParticipant) {
79
83
  if (!proxyEnabled || !proxy) {
80
84
  const errMessage = ERROR_MESSAGES.partySourceFspNotFound
81
85
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
82
86
  }
87
+ step = 'addDfspIdToProxyMapping-1'
83
88
  const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
84
89
  // think,if we should throw error if isCached === false?
85
- logger.info('addDfspIdToProxyMapping is done', { source, proxy, isCached })
90
+ log.info('addDfspIdToProxyMapping is done', { source, proxy, isCached })
86
91
  }
87
92
 
88
93
  if (proxyEnabled && proxy) {
89
94
  const alsReq = utils.alsRequestDto(destination, params) // or source?
95
+ step = 'receivedSuccessResponse-2'
90
96
  const isExists = await proxyCache.receivedSuccessResponse(alsReq)
91
97
  if (!isExists) {
92
- logger.warn('destination is NOT in scheme, and no cached sendToProxiesList', { destination, alsReq })
98
+ log.warn('destination is NOT in scheme, and no cached sendToProxiesList', { destination, alsReq })
93
99
  // think, if we need to throw an error here
94
100
  } else {
95
101
  const mappingPayload = {
96
102
  fspId: source
97
103
  }
104
+ step = 'oracleRequest-3'
98
105
  await oracle.oracleRequest(headers, RestMethods.POST, params, null, mappingPayload, cache)
99
- logger.info('oracle was updated with mappingPayload', { mappingPayload, params })
106
+ log.info('oracle was updated with mappingPayload', { mappingPayload, params })
100
107
  }
101
108
  }
102
-
109
+ step = 'validateParticipant-4'
103
110
  const destinationParticipant = await participant.validateParticipant(destination)
104
111
  if (!destinationParticipant) {
112
+ step = 'lookupProxyByDfspId-5'
105
113
  const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
106
114
  if (!proxyName) {
107
115
  const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
@@ -119,12 +127,21 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
119
127
  partyIdentifier: params.ID,
120
128
  ...(partySubId && { partySubIdOrType: partySubId })
121
129
  }
130
+ step = 'sendRequest-6'
122
131
  await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options)
123
132
 
124
- logger.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo, options })
133
+ log.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo, options })
125
134
  histTimerEnd({ success: true })
126
135
  } catch (err) {
127
- await utils.handleErrorOnSendingCallback(err, headers, params, sendTo)
136
+ const fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
137
+ const extensions = err.extensions || []
138
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
139
+ errorCounter.inc({
140
+ code: fspiopError?.apiErrorCode,
141
+ system,
142
+ operation: 'putPartiesByTypeAndID',
143
+ step
144
+ })
128
145
  histTimerEnd({ success: false })
129
146
  }
130
147
  }
@@ -148,6 +165,7 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
148
165
  'Put parties error by type and id',
149
166
  ['success']
150
167
  ).startTimer()
168
+ const errorCounter = Metrics.getCounter('errorCount')
151
169
  const partySubId = params.SubId
152
170
  const destination = headers[Headers.FSPIOP.DESTINATION]
153
171
  const callbackEndpointType = utils.errorPartyCbType(partySubId)
@@ -157,33 +175,37 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
157
175
 
158
176
  let sendTo
159
177
  let fspiopError
178
+ let step
160
179
 
161
180
  try {
162
181
  const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
163
182
  if (proxy) {
164
183
  if (isNotValidPayeeCase(payload)) {
165
184
  const swappedHeaders = utils.swapSourceDestinationHeaders(headers)
185
+ step = 'oracleRequest-1'
166
186
  await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache)
167
187
  getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, span, cache, proxyCache)
168
188
  // todo: - think if we need to send errorCallback?
169
189
  // - or sentCallback after getPartiesByTypeAndID is done
170
- logger.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID:', { proxy, params, payload })
190
+ log.info('notValidPayee case - deleted Participants and run getPartiesByTypeAndID:', { proxy, params, payload })
171
191
  return
172
192
  }
173
193
 
174
194
  const alsReq = utils.alsRequestDto(destination, params) // or source?
195
+ step = 'receivedErrorResponse-2'
175
196
  const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy)
176
197
  if (!isLast) {
177
- logger.info('got NOT last error callback from proxy:', { proxy, alsReq })
198
+ log.info('got NOT last error callback from proxy:', { proxy, alsReq })
178
199
  return
179
200
  }
180
201
  }
181
-
202
+ step = 'validateParticipant-3'
182
203
  const destinationParticipant = await participant.validateParticipant(destination)
183
204
 
184
205
  if (destinationParticipant) {
185
206
  sendTo = destination
186
207
  } else {
208
+ step = 'lookupProxyByDfspId-4'
187
209
  const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
188
210
  if (!proxyName) {
189
211
  const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
@@ -194,10 +216,18 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
194
216
  const decodedPayload = decodePayload(dataUri, { asParsed: false })
195
217
  await participant.sendErrorToParticipant(sendTo, callbackEndpointType, decodedPayload.body.toString(), headers, params, childSpan)
196
218
 
197
- logger.info('putPartiesErrorByTypeAndID callback was sent', { sendTo })
219
+ log.info('putPartiesErrorByTypeAndID callback was sent', { sendTo })
198
220
  histTimerEnd({ success: true })
199
221
  } catch (err) {
200
- fspiopError = await utils.handleErrorOnSendingCallback(err, headers, params, sendTo)
222
+ fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
223
+ const extensions = err.extensions || []
224
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
225
+ errorCounter.inc({
226
+ code: fspiopError?.apiErrorCode,
227
+ system,
228
+ operation: 'putPartiesErrorByTypeAndID',
229
+ step
230
+ })
201
231
  histTimerEnd({ success: false })
202
232
  } finally {
203
233
  await utils.finishSpanWithError(childSpan, fspiopError)
@@ -1,10 +1,9 @@
1
- const { Enum } = require('@mojaloop/central-services-shared')
1
+ const { Enum, Util: { Hapi } } = require('@mojaloop/central-services-shared')
2
2
  const EventSdk = require('@mojaloop/event-sdk')
3
3
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
4
4
 
5
5
  const participant = require('../../models/participantEndpoint/facade')
6
- const Config = require('../../lib/config')
7
- const { logger } = require('../../lib')
6
+ const { TransformFacades } = require('../../lib')
8
7
 
9
8
  const { FspEndpointTypes } = Enum.EndPoints
10
9
  const { Headers } = Enum.Http
@@ -33,6 +32,13 @@ const finishSpanWithError = async (childSpan, fspiopError) => {
33
32
  }
34
33
  }
35
34
 
35
+ const makePutPartiesErrorPayload = async (config, fspiopError, headers, params) => {
36
+ const body = fspiopError.toApiErrorObject(config.ERROR_HANDLING)
37
+ return config.API_TYPE === Hapi.API_TYPES.iso20022
38
+ ? (await TransformFacades.FSPIOP.parties.putError({ body, headers, params })).body
39
+ : body
40
+ }
41
+
36
42
  const alsRequestDto = (sourceId, params) => ({
37
43
  sourceId,
38
44
  type: params.Type,
@@ -54,13 +60,13 @@ const swapSourceDestinationHeaders = (headers) => {
54
60
  }
55
61
 
56
62
  // change signature to accept object
57
- const handleErrorOnSendingCallback = async (err, headers, params, requester) => {
63
+ const createErrorHandlerOnSendingCallback = (config, logger) => async (err, headers, params, requester) => {
58
64
  try {
59
65
  logger.error('error in sending parties callback', err)
60
66
  const sendTo = requester || headers[Headers.FSPIOP.SOURCE]
61
67
  const errorCallbackEndpointType = errorPartyCbType(params.SubId)
62
68
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
63
- const errInfo = fspiopError.toApiErrorObject(Config.ERROR_HANDLING)
69
+ const errInfo = await makePutPartiesErrorPayload(config, fspiopError, headers, params)
64
70
 
65
71
  await participant.sendErrorToParticipant(sendTo, errorCallbackEndpointType, errInfo, headers, params)
66
72
 
@@ -77,8 +83,9 @@ module.exports = {
77
83
  getPartyCbType,
78
84
  putPartyCbType,
79
85
  errorPartyCbType,
86
+ makePutPartiesErrorPayload,
80
87
  finishSpanWithError,
81
- handleErrorOnSendingCallback,
88
+ createErrorHandlerOnSendingCallback,
82
89
  alsRequestDto,
83
90
  swapSourceDestinationHeaders
84
91
  }
@@ -8,18 +8,10 @@ const {
8
8
  EndPoints: { FspEndpointTypes }
9
9
  } = require('@mojaloop/central-services-shared').Enum
10
10
  const { Tracer } = require('@mojaloop/event-sdk')
11
- const { API_TYPES } = require('@mojaloop/central-services-shared').Util.Hapi
12
11
 
13
- const { TransformFacades } = require('../../lib')
14
12
  const LibUtil = require('../../lib/util')
15
13
  const Config = require('../../lib/config')
16
-
17
- const makeErrorPayload = async (headers, params) => {
18
- const body = createFSPIOPError(FSPIOPErrorCodes.EXPIRED_ERROR).toApiErrorObject(Config.ERROR_HANDLING)
19
- return Config.API_TYPE === API_TYPES.iso20022
20
- ? (await TransformFacades.FSPIOP.parties.putError({ body, headers, params })).body
21
- : body
22
- }
14
+ const partiesUtils = require('../parties/utils')
23
15
 
24
16
  const timeoutCallbackDto = async ({ destination, partyId, partyType }) => {
25
17
  const headers = {
@@ -30,8 +22,10 @@ const timeoutCallbackDto = async ({ destination, partyId, partyType }) => {
30
22
  ID: partyId,
31
23
  Type: partyType
32
24
  }
25
+ const error = createFSPIOPError(FSPIOPErrorCodes.EXPIRED_ERROR)
26
+
33
27
  const dto = {
34
- errorInformation: await makeErrorPayload(headers, params),
28
+ errorInformation: await partiesUtils.makePutPartiesErrorPayload(Config, error, headers, params),
35
29
  headers,
36
30
  params,
37
31
  endpointType: FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTIES_PUT_ERROR
@@ -57,20 +57,31 @@ const sendTimeoutCallback = async (cacheKey) => {
57
57
  'Egress - Interscheme parties lookup timeout callback',
58
58
  ['success']
59
59
  ).startTimer()
60
-
60
+ const errorCounter = Metrics.getCounter('errorCount')
61
+ let step
61
62
  const [, destination, partyType, partyId] = cacheKey.split(':')
62
63
  const { errorInformation, params, headers, endpointType, span } = await timeoutCallbackDto({ destination, partyId, partyType })
63
64
  logger.debug('sendTimeoutCallback details:', { destination, partyType, partyId, cacheKey })
64
65
 
65
66
  try {
67
+ step = 'validateParticipant-1'
66
68
  await validateParticipant(destination)
67
69
  await span.audit({ headers, errorInformation }, AuditEventAction.start)
70
+ step = 'sendErrorToParticipant-2'
68
71
  await Participant.sendErrorToParticipant(destination, endpointType, errorInformation, headers, params, undefined, span)
69
72
  histTimerEnd({ success: true })
70
73
  } catch (err) {
71
74
  logger.warn('error in sendTimeoutCallback: ', err)
72
75
  histTimerEnd({ success: false })
73
76
  const fspiopError = reformatFSPIOPError(err)
77
+ const extensions = err.extensions || []
78
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
79
+ errorCounter.inc({
80
+ code: fspiopError?.apiErrorCode,
81
+ system,
82
+ operation: 'sendTimeoutCallback',
83
+ step
84
+ })
74
85
  await finishSpan(span, fspiopError)
75
86
  throw fspiopError
76
87
  }
@@ -35,7 +35,7 @@ const Package = require('../../package.json')
35
35
  const Server = require('../server')
36
36
  const { HANDLER_TYPES } = require('../constants')
37
37
  const Config = require('../lib/config')
38
- const logger = require('../lib').loggerFactory('ALS-timeout-handler')
38
+ const log = require('../lib').logger.child('ALS-timeout-handler')
39
39
 
40
40
  const Program = new Command()
41
41
 
@@ -51,16 +51,16 @@ Program.command('handlers')
51
51
  const handlers = []
52
52
 
53
53
  if (args.timeout) {
54
- logger.debug('CLI: Executing --timeout')
54
+ log.debug('CLI: Executing --timeout')
55
55
  handlers.push(HANDLER_TYPES.TIMEOUT)
56
56
  }
57
57
 
58
58
  if (handlers.length === 0) {
59
- logger.debug('CLI: No handlers specified')
59
+ log.debug('CLI: No handlers specified')
60
60
  return
61
61
  }
62
62
 
63
- module.exports = await Server.initializeHandlers(handlers, Config, logger)
63
+ module.exports = await Server.initializeHandlers(handlers, Config, log)
64
64
  })
65
65
 
66
66
  if (Array.isArray(process.argv) && process.argv.length > 2) {
@@ -18,7 +18,7 @@
18
18
  const Hapi = require('@hapi/hapi')
19
19
  const Metrics = require('@mojaloop/central-services-metrics')
20
20
  const { plugin: HealthPlugin } = require('./plugins/health')
21
- const { plugin: MetricsPlugin } = require('./plugins/metrics')
21
+ const MetricsPlugin = require('@mojaloop/central-services-metrics').plugin
22
22
  const { logger } = require('../../lib')
23
23
 
24
24
  let server
@@ -890,13 +890,14 @@ components:
890
890
  schemas:
891
891
  CorrelationId:
892
892
  title: CorrelationId
893
- pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
894
893
  type: string
895
- description: Identifier that correlates all messages of the same sequence. The
896
- API data type UUID (Universally Unique Identifier) is a JSON String in canonical
897
- format, conforming to RFC 4122, that is restricted by a regular expression
898
- for interoperability reasons. An UUID is always 36 characters long, 32 hexadecimal
899
- symbols and 4 dashes (‘-‘).
894
+ pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$|^[0-9A-HJKMNP-TV-Z]{26}$
895
+ description: >-
896
+ Identifier that correlates all messages of the same sequence.
897
+ The supported identifiers formats are for
898
+ lowercase [UUID](https://datatracker.ietf.org/doc/html/rfc9562) and
899
+ uppercase [ULID](https://github.com/ulid/spec)
900
+ example: 'b51ec534-ee48-4575-b6a9-ead2955b8069'
900
901
  Currency:
901
902
  title: CurrencyEnum
902
903
  maxLength: 3
@@ -889,13 +889,14 @@ components:
889
889
  schemas:
890
890
  CorrelationId:
891
891
  title: CorrelationId
892
- pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
893
892
  type: string
894
- description: Identifier that correlates all messages of the same sequence. The
895
- API data type UUID (Universally Unique Identifier) is a JSON String in canonical
896
- format, conforming to RFC 4122, that is restricted by a regular expression
897
- for interoperability reasons. An UUID is always 36 characters long, 32 hexadecimal
898
- symbols and 4 dashes (‘-‘).
893
+ pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$|^[0-9A-HJKMNP-TV-Z]{26}$
894
+ description: >-
895
+ Identifier that correlates all messages of the same sequence.
896
+ The supported identifiers formats are for
897
+ lowercase [UUID](https://datatracker.ietf.org/doc/html/rfc9562) and
898
+ uppercase [ULID](https://github.com/ulid/spec)
899
+ example: 'b51ec534-ee48-4575-b6a9-ead2955b8069'
899
900
  Currency:
900
901
  title: CurrencyEnum
901
902
  maxLength: 3