account-lookup-service 15.6.0-snapshot.2 → 16.1.0-iso.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE.md +3 -4
  3. package/README.md +2 -0
  4. package/audit-ci.jsonc +2 -27
  5. package/config/default.json +1 -0
  6. package/docker/mock-proxy/README.md +21 -0
  7. package/docker/mock-proxy/src/config.ts +7 -0
  8. package/docs/Proxy/Discovery.md +20 -0
  9. package/docs/Proxy/FXAPI_POC_payer_conversion_RECEIVE.plantuml +577 -0
  10. package/docs/Proxy/FXAPI_POC_payer_conversion_SEND.plantuml +1423 -0
  11. package/docs/Proxy/P2P.md +11 -0
  12. package/docs/Proxy/Proxy pattern - Happy path.plantuml +98 -0
  13. package/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.plantuml +105 -0
  14. package/docs/Proxy/Proxy pattern - Lazy Discovery - No Oracles.png +0 -0
  15. package/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.plantuml +130 -0
  16. package/docs/Proxy/Proxy pattern - Lazy Discovery - Oracles.png +0 -0
  17. package/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.plantuml +72 -0
  18. package/docs/Proxy/Proxy pattern - Lazy Discovery Identifier Cache Invalid.png +0 -0
  19. package/docs/Proxy/Proxy pattern - P2P.plantuml +186 -0
  20. package/docs/Proxy/Proxy pattern - P2P.png +0 -0
  21. package/docs/Proxy/Proxy pattern - Unhappy path.plantuml +103 -0
  22. package/docs/Proxy/Proxy pattern - Unhappy path.png +0 -0
  23. package/docs/Proxy/Proxy pattern - happy path.png +0 -0
  24. package/docs/Proxy/Readme.md +39 -0
  25. package/docs/Proxy/SettingUpProxys.plantuml +31 -0
  26. package/docs/Proxy/SettingUpProxys.png +0 -0
  27. package/package.json +33 -18
  28. package/src/constants.js +2 -1
  29. package/src/domain/oracle/oracle.js +63 -4
  30. package/src/domain/participants/participants.js +246 -129
  31. package/src/domain/parties/getPartiesByTypeAndID.js +28 -7
  32. package/src/domain/parties/parties.js +43 -14
  33. package/src/domain/timeout/index.js +12 -1
  34. package/src/handlers/index.js +4 -4
  35. package/src/handlers/monitoring/index.js +1 -1
  36. package/src/interface/api-swagger-iso20022-parties.yaml +7 -6
  37. package/src/interface/api-swagger.yaml +7 -6
  38. package/src/lib/config.js +2 -1
  39. package/src/lib/db.js +2 -1
  40. package/src/models/currency/currency.js +10 -1
  41. package/src/models/endpointType/endpointType.js +10 -1
  42. package/src/models/oracle/facade.js +42 -16
  43. package/src/models/oracle/oracleEndpoint.js +65 -10
  44. package/src/models/oracle/oracleEndpointCached.js +29 -9
  45. package/src/models/participantEndpoint/facade.js +40 -4
  46. package/src/models/partyIdType/partyIdType.js +10 -1
  47. package/src/plugins.js +8 -21
  48. package/src/server.js +11 -0
  49. package/test/fixtures/index.js +30 -6
  50. package/test/unit/api/health.test.js +3 -0
  51. package/test/unit/api/participants/participants.test.js +5 -7
  52. package/test/unit/api/participants/{Type}/{ID}/{SubId}.test.js +0 -3
  53. package/test/unit/api/participants/{Type}/{ID}.test.js +0 -3
  54. package/test/unit/api/participants.test.js +36 -3
  55. package/test/unit/domain/oracle/oracle.test.js +8 -0
  56. package/test/unit/domain/participants/participants.test.js +83 -48
  57. package/test/unit/domain/parties/parties.test.js +8 -0
  58. package/test/unit/domain/timeout/index.test.js +8 -0
  59. package/test/unit/lib/config.test.js +7 -0
  60. package/test/unit/mocks.js +16 -11
  61. package/test/unit/models/oracle/oracleEndpointCached.test.js +32 -0
  62. package/test/unit/plugins.test.js +2 -2
  63. package/test/util/apiClients/BasicApiClient.js +2 -2
  64. package/src/lib/requestLogger.js +0 -54
  65. package/src/metrics/handler.js +0 -33
  66. package/src/metrics/plugin.js +0 -52
  67. package/src/metrics/routes.js +0 -43
  68. package/test/unit/lib/requestLogger.test.js +0 -115
@@ -28,7 +28,6 @@
28
28
 
29
29
  const Enums = require('@mojaloop/central-services-shared').Enum
30
30
  const { decodePayload } = require('@mojaloop/central-services-shared').Util.StreamingProtocol
31
- const Logger = require('@mojaloop/central-services-logger')
32
31
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
33
32
  const EventSdk = require('@mojaloop/event-sdk')
34
33
  const Metrics = require('@mojaloop/central-services-metrics')
@@ -36,6 +35,10 @@ const Metrics = require('@mojaloop/central-services-metrics')
36
35
  const oracle = require('../../models/oracle/facade')
37
36
  const participant = require('../../models/participantEndpoint/facade')
38
37
  const Config = require('../../lib/config')
38
+ const { logger } = require('../../lib')
39
+ const { ERROR_MESSAGES } = require('../../constants')
40
+
41
+ const { FSPIOPErrorCodes } = ErrorHandler.Enums
39
42
 
40
43
  /**
41
44
  * @function getParticipantsByTypeAndID
@@ -55,57 +58,97 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
55
58
  'Get participants by ID',
56
59
  ['success']
57
60
  ).startTimer()
61
+ const errorCounter = Metrics.getCounter('errorCount')
62
+ const log = logger.child('getParticipantsByTypeAndID')
58
63
  const type = params.Type
59
- const partySubIdOrType = params.SubId || undefined
60
- const requesterName = headers[Enums.Http.Headers.FSPIOP.SOURCE]
61
- const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
62
- const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
64
+ const partySubIdOrType = params.SubId
65
+ const source = headers[Enums.Http.Headers.FSPIOP.SOURCE]
66
+ const callbackEndpointType = partySubIdOrType
67
+ ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT
68
+ : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
69
+ const errorCallbackEndpointType = params.SubId
70
+ ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
71
+ : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
72
+
63
73
  let fspiopError
74
+ let step
75
+
64
76
  try {
65
- Logger.isInfoEnabled && Logger.info('getParticipantsByTypeAndID::begin')
66
- const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan)
67
- if (requesterParticipantModel) {
68
- const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
69
- if (response && response.data && Array.isArray(response.data.partyList) && response.data.partyList.length > 0) {
70
- let options = {
71
- partyIdType: type,
72
- partyIdentifier: params.ID
73
- }
74
- options = partySubIdOrType ? { ...options, partySubIdOrType } : options
75
- const payload = {
76
- fspId: response.data.partyList[0].fspId
77
- }
78
- const clonedHeaders = { ...headers }
79
- if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') {
80
- clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE]
81
- }
82
- clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
83
- await participant.sendRequest(clonedHeaders, requesterName, callbackEndpointType, Enums.Http.RestMethods.PUT, payload, options, childSpan)
84
- } else {
85
- await participant.sendErrorToParticipant(requesterName, errorCallbackEndpointType,
86
- ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND).toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan)
77
+ log.info('processing started...', { source, params })
78
+ step = 'validateParticipant-1'
79
+ const requesterParticipantModel = await participant.validateParticipant(source)
80
+
81
+ if (!requesterParticipantModel) {
82
+ const errMessage = ERROR_MESSAGES.sourceFspNotFound
83
+ logger.warn(errMessage, { requesterName: source })
84
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
85
+ }
86
+ step = 'oracleRequest-2'
87
+ const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache, true)
88
+ if (response?.data && Array.isArray(response.data.partyList) && response.data.partyList.length > 0) {
89
+ const options = {
90
+ partyIdType: type,
91
+ partyIdentifier: params.ID,
92
+ ...(partySubIdOrType && { partySubIdOrType })
93
+ }
94
+ const payload = {
95
+ fspId: response.data.partyList[0].fspId
96
+ }
97
+ const clonedHeaders = { ...headers }
98
+ if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') {
99
+ clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE]
87
100
  }
88
- Logger.isInfoEnabled && Logger.info('getParticipantsByTypeAndID::end')
101
+ clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
102
+ step = 'sendRequest-3'
103
+ await participant.sendRequest(
104
+ clonedHeaders,
105
+ source,
106
+ callbackEndpointType,
107
+ Enums.Http.RestMethods.PUT,
108
+ payload,
109
+ options,
110
+ childSpan
111
+ )
89
112
  } else {
90
- Logger.isErrorEnabled && Logger.error('Requester FSP not found')
91
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found')
113
+ step = 'sendErrorToParticipant-4'
114
+ await participant.sendErrorToParticipant(
115
+ source,
116
+ errorCallbackEndpointType,
117
+ ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND).toApiErrorObject(Config.ERROR_HANDLING),
118
+ headers,
119
+ params,
120
+ childSpan
121
+ )
92
122
  }
123
+ log.info('processing finished', { source, params })
124
+
93
125
  if (childSpan && !childSpan.isFinished) {
94
126
  await childSpan.finish()
95
127
  }
96
128
  } catch (err) {
97
- Logger.isErrorEnabled && Logger.error(err)
129
+ log.warn('error in getParticipantsByTypeAndID', err)
98
130
  fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
131
+ const extensions = err.extensions || []
132
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
133
+ errorCounter.inc({
134
+ code: fspiopError?.apiErrorCode?.code,
135
+ system,
136
+ operation: 'getParticipantsByTypeAndID',
137
+ step
138
+ })
99
139
  try {
100
- const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
101
140
  await participant.sendErrorToParticipant(
102
- headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
103
- fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan)
141
+ headers[Enums.Http.Headers.FSPIOP.SOURCE],
142
+ errorCallbackEndpointType,
143
+ fspiopError.toApiErrorObject(Config.ERROR_HANDLING),
144
+ headers,
145
+ params,
146
+ childSpan
147
+ )
104
148
  } catch (exc) {
105
- fspiopError = ErrorHandler.Factory.reformatFSPIOPError(exc)
106
149
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
107
150
  // we've already sent a sync response- we cannot throw.
108
- Logger.isErrorEnabled && Logger.error(exc)
151
+ log.error('error in final participant.sendErrorToParticipant', exc)
109
152
  }
110
153
  } finally {
111
154
  if (childSpan && !childSpan.isFinished && fspiopError) {
@@ -137,15 +180,19 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
137
180
  'Put participants by type and ID',
138
181
  ['success']
139
182
  ).startTimer()
183
+ const errorCounter = Metrics.getCounter('errorCount')
184
+ let step
140
185
  try {
141
- Logger.isInfoEnabled && Logger.info('putParticipantsByTypeAndID::begin')
186
+ logger.info('putParticipantsByTypeAndID::begin')
142
187
  const type = params.Type
143
188
  const partySubIdOrType = params.SubId || undefined
144
189
  const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
145
190
  const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
146
191
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
192
+ step = 'validateParticipant-1'
147
193
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE])
148
194
  if (requesterParticipantModel) {
195
+ step = 'oracleRequest-2'
149
196
  const response = await oracle.oracleRequest(headers, method, params, undefined, payload, cache)
150
197
  if (response && response.data) {
151
198
  const responsePayload = {
@@ -171,32 +218,45 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
171
218
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId
172
219
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
173
220
  }
221
+ step = 'sendRequest-3'
174
222
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options)
175
223
  } else {
224
+ step = 'sendErrorToParticipant-4'
176
225
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
177
226
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
178
227
  }
179
228
  } else {
180
- Logger.isErrorEnabled && Logger.error('Requester FSP not found')
181
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found')
229
+ logger.warn(ERROR_MESSAGES.sourceFspNotFound)
230
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.sourceFspNotFound)
182
231
  }
183
232
  } else {
233
+ step = 'sendErrorToParticipant-5'
184
234
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
185
235
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
186
236
  }
187
- Logger.isInfoEnabled && Logger.info('putParticipantsByTypeAndID::end')
237
+ logger.info('putParticipantsByTypeAndID::end')
188
238
  histTimerEnd({ success: true })
189
239
  } catch (err) {
190
- Logger.isErrorEnabled && Logger.error(err)
240
+ logger.error('error in putParticipantsByTypeAndID:', err)
241
+ let fspiopError
191
242
  try {
192
243
  const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
244
+ fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
193
245
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
194
- ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
246
+ fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params)
195
247
  } catch (exc) {
196
248
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
197
249
  // we've already sent a sync response- we cannot throw.
198
- Logger.isErrorEnabled && Logger.error(exc)
250
+ logger.error('error in participant.sendErrorToParticipant:', exc)
199
251
  }
252
+ const extensions = err.extensions || []
253
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
254
+ errorCounter.inc({
255
+ code: fspiopError?.apiErrorCode?.code,
256
+ system,
257
+ operation: 'putParticipantsByTypeAndID',
258
+ step
259
+ })
200
260
  histTimerEnd({ success: false })
201
261
  }
202
262
  }
@@ -220,14 +280,18 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
220
280
  'Put participants error by type and ID',
221
281
  ['success']
222
282
  ).startTimer()
283
+ const errorCounter = Metrics.getCounter('errorCount')
284
+ let step
223
285
  try {
224
286
  const partySubIdOrType = params.SubId || undefined
225
287
  const callbackEndpointType = partySubIdOrType
226
288
  ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
227
289
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
290
+ step = 'validateParticipant-1'
228
291
  const destinationParticipant = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION])
229
292
  if (destinationParticipant) {
230
293
  const decodedPayload = decodePayload(dataUri, { asParsed: false })
294
+ step = 'sendErrorToParticipant-2'
231
295
  await participant.sendErrorToParticipant(
232
296
  headers[Enums.Http.Headers.FSPIOP.DESTINATION],
233
297
  callbackEndpointType,
@@ -236,6 +300,7 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
236
300
  params
237
301
  )
238
302
  } else {
303
+ step = 'sendErrorToParticipant-3'
239
304
  await participant.sendErrorToParticipant(
240
305
  headers[Enums.Http.Headers.FSPIOP.SOURCE],
241
306
  callbackEndpointType,
@@ -247,20 +312,31 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
247
312
  }
248
313
  histTimerEnd({ success: true })
249
314
  } catch (err) {
250
- Logger.isErrorEnabled && Logger.error(err)
315
+ logger.error('error in putParticipantsErrorByTypeAndID:', err)
251
316
  try {
252
- const callbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
317
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
318
+ const callbackEndpointType = params.SubId
319
+ ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
320
+ : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
253
321
  await participant.sendErrorToParticipant(
254
322
  headers[Enums.Http.Headers.FSPIOP.SOURCE],
255
323
  callbackEndpointType,
256
- ErrorHandler.Factory.reformatFSPIOPError(err).toApiErrorObject(),
324
+ fspiopError.toApiErrorObject(),
257
325
  headers,
258
326
  params
259
327
  )
328
+ const extensions = err.extensions || []
329
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
330
+ errorCounter.inc({
331
+ code: fspiopError?.apiErrorCode?.code,
332
+ system,
333
+ operation: 'putParticipantsErrorByTypeAndID',
334
+ step
335
+ })
260
336
  } catch (exc) {
261
337
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
262
338
  // we've already sent a sync response- we cannot throw.
263
- Logger.isErrorEnabled && Logger.error(exc)
339
+ logger.error('error in participant.sendErrorToParticipant:', exc)
264
340
  }
265
341
  histTimerEnd({ success: false })
266
342
  }
@@ -283,10 +359,12 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
283
359
  'Post participants',
284
360
  ['success']
285
361
  ).startTimer()
362
+ const errorCounter = Metrics.getCounter('errorCount')
286
363
  const childSpan = span ? span.getChild('postParticipants') : undefined
287
364
  let fspiopError
365
+ let step
288
366
  try {
289
- Logger.isInfoEnabled && Logger.info('postParticipants::begin')
367
+ logger.info('postParticipants::begin')
290
368
  const type = params.Type
291
369
  const partySubIdOrType = params.SubId
292
370
  const callbackEndpointType = partySubIdOrType
@@ -297,8 +375,10 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
297
375
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
298
376
 
299
377
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
378
+ step = 'validateParticipant-1'
300
379
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan)
301
380
  if (requesterParticipantModel) {
381
+ step = 'oracleRequest-2'
302
382
  const response = await oracle.oracleRequest(headers, method, params, undefined, payload, cache)
303
383
  if (response && response.status === Enums.Http.ReturnCodes.CREATED.CODE) {
304
384
  const responsePayload = {
@@ -324,18 +404,21 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
324
404
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId
325
405
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
326
406
  }
407
+ step = 'sendRequest-3'
327
408
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options, childSpan)
328
409
  if (childSpan && !childSpan.isFinished) {
329
410
  await childSpan.finish()
330
411
  }
331
412
  } else {
332
413
  fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
414
+ step = 'sendErrorToParticipant-4'
333
415
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
334
416
  fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan)
335
417
  }
336
418
  } else {
337
- fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found')
338
- Logger.isErrorEnabled && Logger.error('Requester FSP not found')
419
+ const errMessage = ERROR_MESSAGES.sourceFspNotFound
420
+ logger.error(errMessage)
421
+ fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
339
422
  throw fspiopError
340
423
  }
341
424
  } else {
@@ -343,11 +426,19 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
343
426
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
344
427
  fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan)
345
428
  }
346
- Logger.isInfoEnabled && Logger.info('postParticipants::end')
429
+ logger.info('postParticipants::end')
347
430
  histTimerEnd({ success: true })
348
431
  } catch (err) {
349
- Logger.isErrorEnabled && Logger.error(`error in postParticipants: ${err?.message}`)
432
+ logger.error('error in postParticipants:', err)
350
433
  fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
434
+ const extensions = err.extensions || []
435
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
436
+ errorCounter.inc({
437
+ code: fspiopError?.apiErrorCode?.code,
438
+ system,
439
+ operation: 'postParticipants',
440
+ step
441
+ })
351
442
  try {
352
443
  const errorCallbackEndpointType = params.SubId
353
444
  ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
@@ -357,7 +448,7 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
357
448
  } catch (exc) {
358
449
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
359
450
  // we've already sent a sync response- we cannot throw.
360
- Logger.isErrorEnabled && Logger.error(`failed to handle exception in postParticipants due to error: ${exc?.stack}`)
451
+ logger.error('failed to handle exception in postParticipants:', exc)
361
452
  }
362
453
  histTimerEnd({ success: false })
363
454
  } finally {
@@ -385,100 +476,108 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => {
385
476
  'Post participants batch',
386
477
  ['success']
387
478
  ).startTimer()
479
+ const errorCounter = Metrics.getCounter('errorCount')
480
+ const requestId = requestPayload.requestId
481
+ const log = logger.child({ context: 'postParticipantsBatch', requestId })
388
482
  const childSpan = span ? span.getChild('postParticipantsBatch') : undefined
389
483
  let fspiopError
484
+ let step
390
485
  try {
391
- Logger.isInfoEnabled && Logger.info('postParticipantsBatch::begin')
486
+ log.info('postParticipantsBatch::begin', { headers, requestPayload })
392
487
  const typeMap = new Map()
393
488
  const overallReturnList = []
394
- const requestId = requestPayload.requestId
489
+ const pushPartyError = (party, errCode) => {
490
+ log.debug('party error details:', { party, errCode })
491
+ overallReturnList.push({
492
+ partyId: party,
493
+ ...ErrorHandler.Factory.createFSPIOPError(errCode, undefined, undefined, undefined, [{
494
+ key: party.partyIdType,
495
+ value: party.partyIdentifier
496
+ }]).toApiErrorObject(Config.ERROR_HANDLING)
497
+ })
498
+ }
499
+ step = 'validateParticipant-1'
395
500
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan)
396
- if (requesterParticipantModel) {
397
- for (const party of requestPayload.partyList) {
398
- if (Object.values(Enums.Accounts.PartyAccountTypes).includes(party.partyIdType)) {
399
- party.currency = requestPayload.currency
400
- if (party.fspId === headers[Enums.Http.Headers.FSPIOP.SOURCE]) {
401
- if (typeMap.get(party.partyIdType)) {
402
- const partyList = typeMap.get(party.partyIdType)
403
- partyList.push(party)
404
- typeMap.set(party.partyIdType, partyList)
405
- } else {
406
- typeMap.set(party.partyIdType, [party])
407
- }
408
- } else {
409
- overallReturnList.push(ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND, undefined, undefined, undefined, [{
410
- key: party.partyIdType,
411
- value: party.partyIdentifier
412
- }]).toApiErrorObject(Config.ERROR_HANDLING))
413
- }
414
- } else {
415
- overallReturnList.push(ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR, undefined, undefined, undefined, [{
416
- key: party.partyIdType,
417
- value: party.partyIdentifier
418
- }]).toApiErrorObject(Config.ERROR_HANDLING))
419
- }
420
- }
501
+ if (!requesterParticipantModel) {
502
+ const errMessage = ERROR_MESSAGES.sourceFspNotFound
503
+ log.error(errMessage)
504
+ fspiopError = ErrorHandler.Factory.createFSPIOPError(FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
505
+ throw fspiopError
506
+ }
421
507
 
422
- for (const [key, value] of typeMap) {
423
- const payload = {
424
- requestId,
425
- partyList: value
426
- }
427
- Logger.isInfoEnabled && Logger.info(`postParticipantsBatch::oracleBatchRequest::type=${key}`)
428
- const response = await oracle.oracleBatchRequest(headers, method, requestPayload, key, payload)
429
- if (response && (response.data !== null || response.data !== undefined)) {
430
- if (Array.isArray(response.data.partyList) && response.data.partyList.length > 0) {
431
- for (const party of response.data.partyList) {
432
- party.partyId.currency = undefined
433
- overallReturnList.push(party)
434
- }
508
+ for (const party of requestPayload.partyList) {
509
+ if (Object.values(Enums.Accounts.PartyAccountTypes).includes(party.partyIdType)) {
510
+ party.currency = requestPayload.currency
511
+ if (party.fspId === headers[Enums.Http.Headers.FSPIOP.SOURCE]) {
512
+ if (typeMap.get(party.partyIdType)) {
513
+ const partyList = typeMap.get(party.partyIdType)
514
+ partyList.push(party)
515
+ typeMap.set(party.partyIdType, partyList)
435
516
  } else {
436
- for (const party of value) {
437
- overallReturnList.push(ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR, undefined, undefined, undefined, [{
438
- key: party.partyIdType,
439
- value: party.partyIdentifier
440
- }]).toApiErrorObject(Config.ERROR_HANDLING))
441
- }
517
+ typeMap.set(party.partyIdType, [party])
442
518
  }
443
519
  } else {
444
- for (const party of value) {
445
- overallReturnList.push(ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR, undefined, undefined, undefined, [{
446
- key: party.partyIdType,
447
- value: party.partyIdentifier
448
- }]).toApiErrorObject(Config.ERROR_HANDLING))
449
- }
520
+ pushPartyError(party, FSPIOPErrorCodes.PARTY_NOT_FOUND)
450
521
  }
522
+ } else {
523
+ pushPartyError(party, FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
451
524
  }
525
+ }
526
+
527
+ for (const [key, value] of typeMap) {
452
528
  const payload = {
453
- partyList: overallReturnList,
454
- currency: requestPayload.currency
529
+ requestId,
530
+ partyList: value
455
531
  }
456
- const clonedHeaders = { ...headers }
457
- if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') {
458
- clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.partyList[0].partyId.fspId
459
- clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
460
- }
461
- await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT, Enums.Http.RestMethods.PUT, payload, { requestId }, childSpan)
462
- if (childSpan && !childSpan.isFinished) {
463
- await childSpan.finish()
532
+ log.info(`postParticipantsBatch::oracleBatchRequest::type=${key}`, { payload })
533
+ step = 'oracleBatchRequest-2'
534
+ const response = await oracle.oracleBatchRequest(headers, method, requestPayload, key, payload)
535
+ if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
536
+ for (const party of response.data.partyList) {
537
+ party.partyId.currency = undefined
538
+ overallReturnList.push(party)
539
+ }
540
+ } else {
541
+ for (const party of value) {
542
+ pushPartyError(party, FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
543
+ }
464
544
  }
465
- Logger.isInfoEnabled && Logger.info('postParticipantsBatch::end')
466
- } else {
467
- Logger.isErrorEnabled && Logger.error('Requester FSP not found')
468
- fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found')
469
- throw fspiopError
470
545
  }
546
+
547
+ const payload = {
548
+ partyList: overallReturnList,
549
+ currency: requestPayload.currency
550
+ }
551
+ const clonedHeaders = { ...headers }
552
+ if (!clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] || clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] === '') {
553
+ clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = headers[Enums.Http.Headers.FSPIOP.SOURCE]
554
+ clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
555
+ }
556
+ await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT, Enums.Http.RestMethods.PUT, payload, { requestId }, childSpan)
557
+ if (childSpan && !childSpan.isFinished) {
558
+ await childSpan.finish()
559
+ }
560
+ log.info('postParticipantsBatch::end')
561
+
471
562
  histTimerEnd({ success: true })
472
563
  } catch (err) {
473
- Logger.isErrorEnabled && Logger.error(err)
564
+ log.error('error in postParticipantsBatch', err)
474
565
  fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
566
+ const extensions = err.extensions || []
567
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
568
+ errorCounter.inc({
569
+ code: fspiopError?.apiErrorCode?.code,
570
+ system,
571
+ operation: 'postParticipantsBatch',
572
+ step
573
+ })
475
574
  try {
476
575
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT_ERROR,
477
576
  fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, undefined, requestPayload)
478
577
  } catch (exc) {
479
578
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
480
579
  // we've already sent a sync response- we cannot throw.
481
- Logger.isErrorEnabled && Logger.error(exc)
580
+ log.error('error in participant.sendErrorToParticipant', exc)
482
581
  }
483
582
  histTimerEnd({ success: false })
484
583
  } finally {
@@ -507,15 +606,20 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
507
606
  'Delete participants',
508
607
  ['success']
509
608
  ).startTimer()
609
+ const errorCounter = Metrics.getCounter('errorCount')
610
+ const log = logger.child('deleteParticipants')
611
+ let step
510
612
  try {
511
- Logger.isInfoEnabled && Logger.info('deleteParticipants::begin')
613
+ log.debug('deleteParticipants::begin', { headers, params })
512
614
  const type = params.Type
513
615
  const partySubIdOrType = params.SubId || undefined
514
616
  const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
515
617
  const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
516
618
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
619
+ step = 'validateParticipant-1'
517
620
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE])
518
621
  if (requesterParticipantModel) {
622
+ step = 'oracleRequest-2'
519
623
  const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
520
624
  if (response) {
521
625
  const responsePayload = {
@@ -529,31 +633,44 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
529
633
  const clonedHeaders = { ...headers }
530
634
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = headers[Enums.Http.Headers.FSPIOP.SOURCE]
531
635
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
636
+ step = 'sendRequest-3'
532
637
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options)
533
638
  } else {
639
+ step = 'sendErrorToParticipant-4'
534
640
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
535
641
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
536
642
  }
537
643
  } else {
538
- Logger.isErrorEnabled && Logger.error('Requester FSP not found')
539
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'Requester FSP not found')
644
+ const errMessage = ERROR_MESSAGES.sourceFspNotFound
645
+ log.error(errMessage)
646
+ throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
540
647
  }
541
648
  } else {
649
+ step = 'sendErrorToParticipant-5'
542
650
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
543
651
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
544
652
  }
545
- Logger.isInfoEnabled && Logger.info('deleteParticipants::end')
653
+ log.info('deleteParticipants::end')
546
654
  histTimerEnd({ success: true })
547
655
  } catch (err) {
548
- Logger.isErrorEnabled && Logger.error(err)
656
+ log.error('error in deleteParticipants', err)
549
657
  try {
658
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR)
550
659
  const errorCallbackEndpointType = params.SubId ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
551
660
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
552
- ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
661
+ fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params)
662
+ const extensions = err.extensions || []
663
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
664
+ errorCounter.inc({
665
+ code: fspiopError?.apiErrorCode?.code,
666
+ system,
667
+ operation: 'deleteParticipants',
668
+ step
669
+ })
553
670
  } catch (exc) {
554
671
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
555
672
  // we've already sent a sync response- we cannot throw.
556
- Logger.isErrorEnabled && Logger.error(exc)
673
+ log.error('error in participant.sendErrorToParticipant', exc)
557
674
  }
558
675
  histTimerEnd({ success: false })
559
676
  }