account-lookup-service 15.6.0-iso.20 → 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.
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": "15.6.0-iso.20",
4
+ "version": "15.6.0-iso.21",
5
5
  "license": "Apache-2.0",
6
6
  "author": "ModusBox",
7
7
  "contributors": [
@@ -93,12 +93,12 @@
93
93
  "@mojaloop/central-services-error-handling": "13.0.2",
94
94
  "@mojaloop/central-services-health": "15.0.0",
95
95
  "@mojaloop/central-services-logger": "11.5.1",
96
- "@mojaloop/central-services-metrics": "12.4.1",
97
- "@mojaloop/central-services-shared": "18.13.0",
96
+ "@mojaloop/central-services-metrics": "12.4.2",
97
+ "@mojaloop/central-services-shared": "18.14.1",
98
98
  "@mojaloop/central-services-stream": "11.3.1",
99
99
  "@mojaloop/database-lib": "11.0.6",
100
100
  "@mojaloop/event-sdk": "14.1.1",
101
- "@mojaloop/inter-scheme-proxy-cache-lib": "2.3.0",
101
+ "@mojaloop/inter-scheme-proxy-cache-lib": "2.3.1",
102
102
  "@mojaloop/ml-schema-transformer-lib": "2.4.1",
103
103
  "@mojaloop/sdk-standard-components": "19.6.2",
104
104
  "@now-ims/hapi-now-auth": "2.1.0",
@@ -106,7 +106,7 @@
106
106
  "ajv-keywords": "5.1.0",
107
107
  "blipp": "4.0.2",
108
108
  "commander": "12.1.0",
109
- "cron": "3.3.0",
109
+ "cron": "3.3.1",
110
110
  "fast-safe-stringify": "^2.1.1",
111
111
  "hapi-auth-bearer-token": "8.0.0",
112
112
  "joi": "17.13.3",
@@ -49,9 +49,15 @@ exports.createOracle = async (payload) => {
49
49
  'Create Oracle',
50
50
  ['success']
51
51
  ).startTimer()
52
+ const errorCounter = Metrics.getCounter('errorCount')
53
+ let step
54
+
52
55
  try {
56
+ step = 'getPartyIdTypeByName-1'
53
57
  const partyIdTypeModel = await partyIdType.getPartyIdTypeByName(payload.oracleIdType)
58
+ step = 'getEndpointTypeByType-2'
54
59
  const endpointTypeModel = await endpointType.getEndpointTypeByType(payload.endpoint.endpointType)
60
+ step = 'getAllOracleEndpointsByMatchCondition-3'
55
61
  const existingActiveOracle = await oracleEndpoint.getAllOracleEndpointsByMatchCondition(
56
62
  payload,
57
63
  partyIdTypeModel.partyIdTypeId,
@@ -77,13 +83,23 @@ exports.createOracle = async (payload) => {
77
83
  oracleEntity.createdBy = 'Admin'
78
84
  oracleEntity.partyIdTypeId = partyIdTypeModel.partyIdTypeId
79
85
  oracleEntity.endpointTypeId = endpointTypeModel.endpointTypeId
86
+ step = 'createOracleEndpoint-4'
80
87
  await oracleEndpoint.createOracleEndpoint(oracleEntity)
81
88
  histTimerEnd({ success: true })
82
89
  return true
83
90
  } catch (err) {
84
91
  histTimerEnd({ success: false })
85
92
  Logger.isErrorEnabled && Logger.error(err)
86
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
93
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
94
+ const extensions = err.extensions || []
95
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
96
+ errorCounter.inc({
97
+ code: fspiopError?.apiErrorCode,
98
+ system,
99
+ operation: 'createOracle',
100
+ step
101
+ })
102
+ throw fspiopError
87
103
  }
88
104
  }
89
105
 
@@ -100,6 +116,8 @@ exports.getOracle = async (query) => {
100
116
  'Get Oracle',
101
117
  ['success']
102
118
  ).startTimer()
119
+ const errorCounter = Metrics.getCounter('errorCount')
120
+ let step
103
121
  try {
104
122
  let oracleEndpointModelList
105
123
  let isCurrency; let isType = false
@@ -111,12 +129,16 @@ exports.getOracle = async (query) => {
111
129
  isType = true
112
130
  }
113
131
  if (isCurrency && isType) {
132
+ step = 'getOracleEndpointByTypeAndCurrency-1'
114
133
  oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByTypeAndCurrency(query.type, query.currency)
115
134
  } else if (isCurrency && !isType) {
135
+ step = 'getOracleEndpointByCurrency-2'
116
136
  oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByCurrency(query.currency)
117
137
  } else if (isType && !isCurrency) {
138
+ step = 'getOracleEndpointByType-3'
118
139
  oracleEndpointModelList = await cachedOracleEndpoint.getOracleEndpointByType(query.type)
119
140
  } else {
141
+ step = 'getAllOracleEndpoint-4'
120
142
  oracleEndpointModelList = await oracleEndpoint.getAllOracleEndpoint()
121
143
  }
122
144
  for (const oracleEndpointModel of oracleEndpointModelList) {
@@ -137,7 +159,16 @@ exports.getOracle = async (query) => {
137
159
  } catch (err) {
138
160
  histTimerEnd({ success: false })
139
161
  Logger.isErrorEnabled && Logger.error(err)
140
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
162
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
163
+ const extensions = err.extensions || []
164
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
165
+ errorCounter.inc({
166
+ code: fspiopError?.apiErrorCode,
167
+ system,
168
+ operation: 'getOracle',
169
+ step
170
+ })
171
+ throw fspiopError
141
172
  }
142
173
  }
143
174
 
@@ -155,11 +186,17 @@ exports.updateOracle = async (params, payload) => {
155
186
  'Update Oracle',
156
187
  ['success']
157
188
  ).startTimer()
189
+ const errorCounter = Metrics.getCounter('errorCount')
190
+ let step
158
191
  try {
192
+ step = 'getOracleEndpointById-1'
159
193
  const currentOracleEndpointList = await oracleEndpoint.getOracleEndpointById(params.ID)
160
194
  if (currentOracleEndpointList.length > 0) {
195
+ step = 'getPartyIdTypeByName-2'
161
196
  const partyIdTypeModel = await partyIdType.getPartyIdTypeByName(payload.oracleIdType)
197
+ step = 'getEndpointTypeByType-3'
162
198
  const endpointTypeModel = await endpointType.getEndpointTypeByType(payload.endpoint.endpointType)
199
+ step = 'getAllOracleEndpointsByMatchCondition-4'
163
200
  const existingActiveOracle = await oracleEndpoint.getAllOracleEndpointsByMatchCondition(
164
201
  payload,
165
202
  partyIdTypeModel.partyIdTypeId,
@@ -175,6 +212,7 @@ exports.updateOracle = async (params, payload) => {
175
212
  const currentOracleEndpoint = currentOracleEndpointList[0]
176
213
  const newOracleEntry = {}
177
214
  if (payload.oracleIdType && payload.oracleIdType !== currentOracleEndpoint.idType) {
215
+ step = 'getPartyIdTypeByName-5'
178
216
  const partyTypeModel = await partyIdType.getPartyIdTypeByName(payload.oracleIdType)
179
217
  newOracleEntry.partyIdTypeId = partyTypeModel.partyIdTypeId
180
218
  }
@@ -182,10 +220,12 @@ exports.updateOracle = async (params, payload) => {
182
220
  newOracleEntry.value = payload.endpoint.value
183
221
  }
184
222
  if (payload.endpoint && payload.endpoint.endpointType && payload.endpoint.endpointType !== currentOracleEndpoint.endpointType) {
223
+ step = 'getEndpointTypeByType-6'
185
224
  const endpointTypeModel = await endpointType.getEndpointTypeByType(payload.endpoint.endpointType)
186
225
  newOracleEntry.endpointTypeId = endpointTypeModel.endpointTypeId
187
226
  }
188
227
  if (payload.currency && payload.currency !== currentOracleEndpoint.currency) {
228
+ step = 'getCurrencyById-7'
189
229
  const currencyModel = await currency.getCurrencyById(payload.currency)
190
230
  if (currencyModel) {
191
231
  newOracleEntry.currencyId = payload.currency
@@ -196,6 +236,7 @@ exports.updateOracle = async (params, payload) => {
196
236
  if (payload.isDefault && payload.isDefault !== currentOracleEndpoint.isDefault) {
197
237
  newOracleEntry.isDefault = payload.isDefault
198
238
  }
239
+ step = 'updateOracleEndpointById-8'
199
240
  await oracleEndpoint.updateOracleEndpointById(params.ID, newOracleEntry)
200
241
  histTimerEnd({ success: true })
201
242
  return true
@@ -205,7 +246,16 @@ exports.updateOracle = async (params, payload) => {
205
246
  } catch (err) {
206
247
  histTimerEnd({ success: false })
207
248
  Logger.isErrorEnabled && Logger.error(err)
208
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
249
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
250
+ const extensions = err.extensions || []
251
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
252
+ errorCounter.inc({
253
+ code: fspiopError?.apiErrorCode,
254
+ system,
255
+ operation: 'updateOracle',
256
+ step
257
+ })
258
+ throw fspiopError
209
259
  }
210
260
  }
211
261
 
@@ -222,6 +272,7 @@ exports.deleteOracle = async (params) => {
222
272
  'Delete Oracle',
223
273
  ['success']
224
274
  ).startTimer()
275
+ const errorCounter = Metrics.getCounter('errorCount')
225
276
  try {
226
277
  await oracleEndpoint.destroyOracleEndpointById(params.ID)
227
278
  histTimerEnd({ success: true })
@@ -229,6 +280,14 @@ exports.deleteOracle = async (params) => {
229
280
  } catch (err) {
230
281
  histTimerEnd({ success: false })
231
282
  Logger.isErrorEnabled && Logger.error(err)
232
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
283
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
284
+ const extensions = err.extensions || []
285
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
286
+ errorCounter.inc({
287
+ code: fspiopError?.apiErrorCode,
288
+ system,
289
+ operation: 'deleteOracle'
290
+ })
291
+ throw fspiopError
233
292
  }
234
293
  }
@@ -58,6 +58,7 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
58
58
  'Get participants by ID',
59
59
  ['success']
60
60
  ).startTimer()
61
+ const errorCounter = Metrics.getCounter('errorCount')
61
62
  const log = logger.child('getParticipantsByTypeAndID')
62
63
  const type = params.Type
63
64
  const partySubIdOrType = params.SubId
@@ -70,9 +71,11 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
70
71
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
71
72
 
72
73
  let fspiopError
74
+ let step
73
75
 
74
76
  try {
75
77
  log.info('processing started...', { source, params })
78
+ step = 'validateParticipant-1'
76
79
  const requesterParticipantModel = await participant.validateParticipant(source)
77
80
 
78
81
  if (!requesterParticipantModel) {
@@ -80,7 +83,7 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
80
83
  logger.warn(errMessage, { requesterName: source })
81
84
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
82
85
  }
83
-
86
+ step = 'oracleRequest-2'
84
87
  const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
85
88
  if (response?.data && Array.isArray(response.data.partyList) && response.data.partyList.length > 0) {
86
89
  const options = {
@@ -96,6 +99,7 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
96
99
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE]
97
100
  }
98
101
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
102
+ step = 'sendRequest-3'
99
103
  await participant.sendRequest(
100
104
  clonedHeaders,
101
105
  source,
@@ -106,6 +110,7 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
106
110
  childSpan
107
111
  )
108
112
  } else {
113
+ step = 'sendErrorToParticipant-4'
109
114
  await participant.sendErrorToParticipant(
110
115
  source,
111
116
  errorCallbackEndpointType,
@@ -123,6 +128,14 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
123
128
  } catch (err) {
124
129
  log.warn('error in getParticipantsByTypeAndID', err)
125
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,
135
+ system,
136
+ operation: 'getParticipantsByTypeAndID',
137
+ step
138
+ })
126
139
  try {
127
140
  await participant.sendErrorToParticipant(
128
141
  headers[Enums.Http.Headers.FSPIOP.SOURCE],
@@ -133,7 +146,6 @@ const getParticipantsByTypeAndID = async (headers, params, method, query, span,
133
146
  childSpan
134
147
  )
135
148
  } catch (exc) {
136
- fspiopError = ErrorHandler.Factory.reformatFSPIOPError(exc)
137
149
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
138
150
  // we've already sent a sync response- we cannot throw.
139
151
  log.error('error in final participant.sendErrorToParticipant', exc)
@@ -168,6 +180,8 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
168
180
  'Put participants by type and ID',
169
181
  ['success']
170
182
  ).startTimer()
183
+ const errorCounter = Metrics.getCounter('errorCount')
184
+ let step
171
185
  try {
172
186
  logger.info('putParticipantsByTypeAndID::begin')
173
187
  const type = params.Type
@@ -175,8 +189,10 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
175
189
  const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
176
190
  const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
177
191
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
192
+ step = 'validateParticipant-1'
178
193
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE])
179
194
  if (requesterParticipantModel) {
195
+ step = 'oracleRequest-2'
180
196
  const response = await oracle.oracleRequest(headers, method, params, undefined, payload, cache)
181
197
  if (response && response.data) {
182
198
  const responsePayload = {
@@ -202,8 +218,10 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
202
218
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId
203
219
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
204
220
  }
221
+ step = 'sendRequest-3'
205
222
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options)
206
223
  } else {
224
+ step = 'sendErrorToParticipant-4'
207
225
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
208
226
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
209
227
  }
@@ -212,6 +230,7 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
212
230
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.sourceFspNotFound)
213
231
  }
214
232
  } else {
233
+ step = 'sendErrorToParticipant-5'
215
234
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
216
235
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
217
236
  }
@@ -219,15 +238,25 @@ const putParticipantsByTypeAndID = async (headers, params, method, payload, cach
219
238
  histTimerEnd({ success: true })
220
239
  } catch (err) {
221
240
  logger.error('error in putParticipantsByTypeAndID:', err)
241
+ let fspiopError
222
242
  try {
223
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)
224
245
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
225
- 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)
226
247
  } catch (exc) {
227
248
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
228
249
  // we've already sent a sync response- we cannot throw.
229
250
  logger.error('error in participant.sendErrorToParticipant:', exc)
230
251
  }
252
+ const extensions = err.extensions || []
253
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
254
+ errorCounter.inc({
255
+ code: fspiopError?.apiErrorCode,
256
+ system,
257
+ operation: 'putParticipantsByTypeAndID',
258
+ step
259
+ })
231
260
  histTimerEnd({ success: false })
232
261
  }
233
262
  }
@@ -251,14 +280,18 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
251
280
  'Put participants error by type and ID',
252
281
  ['success']
253
282
  ).startTimer()
283
+ const errorCounter = Metrics.getCounter('errorCount')
284
+ let step
254
285
  try {
255
286
  const partySubIdOrType = params.SubId || undefined
256
287
  const callbackEndpointType = partySubIdOrType
257
288
  ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
258
289
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
290
+ step = 'validateParticipant-1'
259
291
  const destinationParticipant = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.DESTINATION])
260
292
  if (destinationParticipant) {
261
293
  const decodedPayload = decodePayload(dataUri, { asParsed: false })
294
+ step = 'sendErrorToParticipant-2'
262
295
  await participant.sendErrorToParticipant(
263
296
  headers[Enums.Http.Headers.FSPIOP.DESTINATION],
264
297
  callbackEndpointType,
@@ -267,6 +300,7 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
267
300
  params
268
301
  )
269
302
  } else {
303
+ step = 'sendErrorToParticipant-3'
270
304
  await participant.sendErrorToParticipant(
271
305
  headers[Enums.Http.Headers.FSPIOP.SOURCE],
272
306
  callbackEndpointType,
@@ -280,16 +314,25 @@ const putParticipantsErrorByTypeAndID = async (headers, params, payload, dataUri
280
314
  } catch (err) {
281
315
  logger.error('error in putParticipantsErrorByTypeAndID:', err)
282
316
  try {
317
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
283
318
  const callbackEndpointType = params.SubId
284
319
  ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
285
320
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
286
321
  await participant.sendErrorToParticipant(
287
322
  headers[Enums.Http.Headers.FSPIOP.SOURCE],
288
323
  callbackEndpointType,
289
- ErrorHandler.Factory.reformatFSPIOPError(err).toApiErrorObject(),
324
+ fspiopError.toApiErrorObject(),
290
325
  headers,
291
326
  params
292
327
  )
328
+ const extensions = err.extensions || []
329
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
330
+ errorCounter.inc({
331
+ code: fspiopError?.apiErrorCode,
332
+ system,
333
+ operation: 'putParticipantsErrorByTypeAndID',
334
+ step
335
+ })
293
336
  } catch (exc) {
294
337
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
295
338
  // we've already sent a sync response- we cannot throw.
@@ -316,8 +359,10 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
316
359
  'Post participants',
317
360
  ['success']
318
361
  ).startTimer()
362
+ const errorCounter = Metrics.getCounter('errorCount')
319
363
  const childSpan = span ? span.getChild('postParticipants') : undefined
320
364
  let fspiopError
365
+ let step
321
366
  try {
322
367
  logger.info('postParticipants::begin')
323
368
  const type = params.Type
@@ -330,8 +375,10 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
330
375
  : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
331
376
 
332
377
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
378
+ step = 'validateParticipant-1'
333
379
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan)
334
380
  if (requesterParticipantModel) {
381
+ step = 'oracleRequest-2'
335
382
  const response = await oracle.oracleRequest(headers, method, params, undefined, payload, cache)
336
383
  if (response && response.status === Enums.Http.ReturnCodes.CREATED.CODE) {
337
384
  const responsePayload = {
@@ -357,12 +404,14 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
357
404
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = payload.fspId
358
405
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
359
406
  }
407
+ step = 'sendRequest-3'
360
408
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options, childSpan)
361
409
  if (childSpan && !childSpan.isFinished) {
362
410
  await childSpan.finish()
363
411
  }
364
412
  } else {
365
413
  fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ADD_PARTY_INFO_ERROR)
414
+ step = 'sendErrorToParticipant-4'
366
415
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
367
416
  fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, params, childSpan)
368
417
  }
@@ -382,6 +431,14 @@ const postParticipants = async (headers, method, params, payload, span, cache) =
382
431
  } catch (err) {
383
432
  logger.error('error in postParticipants:', err)
384
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,
438
+ system,
439
+ operation: 'postParticipants',
440
+ step
441
+ })
385
442
  try {
386
443
  const errorCallbackEndpointType = params.SubId
387
444
  ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR
@@ -419,11 +476,12 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => {
419
476
  'Post participants batch',
420
477
  ['success']
421
478
  ).startTimer()
479
+ const errorCounter = Metrics.getCounter('errorCount')
422
480
  const requestId = requestPayload.requestId
423
481
  const log = logger.child({ context: 'postParticipantsBatch', requestId })
424
482
  const childSpan = span ? span.getChild('postParticipantsBatch') : undefined
425
483
  let fspiopError
426
-
484
+ let step
427
485
  try {
428
486
  log.info('postParticipantsBatch::begin', { headers, requestPayload })
429
487
  const typeMap = new Map()
@@ -438,7 +496,7 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => {
438
496
  }]).toApiErrorObject(Config.ERROR_HANDLING)
439
497
  })
440
498
  }
441
-
499
+ step = 'validateParticipant-1'
442
500
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], childSpan)
443
501
  if (!requesterParticipantModel) {
444
502
  const errMessage = ERROR_MESSAGES.sourceFspNotFound
@@ -472,6 +530,7 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => {
472
530
  partyList: value
473
531
  }
474
532
  log.info(`postParticipantsBatch::oracleBatchRequest::type=${key}`, { payload })
533
+ step = 'oracleBatchRequest-2'
475
534
  const response = await oracle.oracleBatchRequest(headers, method, requestPayload, key, payload)
476
535
  if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
477
536
  for (const party of response.data.partyList) {
@@ -504,6 +563,14 @@ const postParticipantsBatch = async (headers, method, requestPayload, span) => {
504
563
  } catch (err) {
505
564
  log.error('error in postParticipantsBatch', err)
506
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,
570
+ system,
571
+ operation: 'postParticipantsBatch',
572
+ step
573
+ })
507
574
  try {
508
575
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_BATCH_PUT_ERROR,
509
576
  fspiopError.toApiErrorObject(Config.ERROR_HANDLING), headers, undefined, requestPayload)
@@ -539,7 +606,9 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
539
606
  'Delete participants',
540
607
  ['success']
541
608
  ).startTimer()
609
+ const errorCounter = Metrics.getCounter('errorCount')
542
610
  const log = logger.child('deleteParticipants')
611
+ let step
543
612
  try {
544
613
  log.debug('deleteParticipants::begin', { headers, params })
545
614
  const type = params.Type
@@ -547,8 +616,10 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
547
616
  const callbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT
548
617
  const errorCallbackEndpointType = partySubIdOrType ? Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_SUB_ID_PUT_ERROR : Enums.EndPoints.FspEndpointTypes.FSPIOP_CALLBACK_URL_PARTICIPANT_PUT_ERROR
549
618
  if (Object.values(Enums.Accounts.PartyAccountTypes).includes(type)) {
619
+ step = 'validateParticipant-1'
550
620
  const requesterParticipantModel = await participant.validateParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE])
551
621
  if (requesterParticipantModel) {
622
+ step = 'oracleRequest-2'
552
623
  const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
553
624
  if (response) {
554
625
  const responsePayload = {
@@ -562,8 +633,10 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
562
633
  const clonedHeaders = { ...headers }
563
634
  clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION] = headers[Enums.Http.Headers.FSPIOP.SOURCE]
564
635
  clonedHeaders[Enums.Http.Headers.FSPIOP.SOURCE] = Config.HUB_NAME
636
+ step = 'sendRequest-3'
565
637
  await participant.sendRequest(clonedHeaders, clonedHeaders[Enums.Http.Headers.FSPIOP.DESTINATION], callbackEndpointType, Enums.Http.RestMethods.PUT, responsePayload, options)
566
638
  } else {
639
+ step = 'sendErrorToParticipant-4'
567
640
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
568
641
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
569
642
  }
@@ -573,6 +646,7 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
573
646
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
574
647
  }
575
648
  } else {
649
+ step = 'sendErrorToParticipant-5'
576
650
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
577
651
  ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR).toApiErrorObject(Config.ERROR_HANDLING), headers, params)
578
652
  }
@@ -581,9 +655,18 @@ const deleteParticipants = async (headers, params, method, query, cache) => {
581
655
  } catch (err) {
582
656
  log.error('error in deleteParticipants', err)
583
657
  try {
658
+ const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.DELETE_PARTY_INFO_ERROR)
584
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
585
660
  await participant.sendErrorToParticipant(headers[Enums.Http.Headers.FSPIOP.SOURCE], errorCallbackEndpointType,
586
- 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,
666
+ system,
667
+ operation: 'deleteParticipants',
668
+ step
669
+ })
587
670
  } catch (exc) {
588
671
  // We can't do anything else here- we _must_ handle all errors _within_ this function because
589
672
  // we've already sent a sync response- we cannot throw.
@@ -87,6 +87,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
87
87
  'Get party by Type and Id',
88
88
  ['success']
89
89
  ).startTimer()
90
+ const errorCounter = Metrics.getCounter('errorCount')
90
91
  const proxyEnabled = !!(Config.PROXY_CACHE_CONFIG.enabled && proxyCache)
91
92
  const type = params.Type
92
93
  const partySubId = params.SubId
@@ -99,7 +100,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
99
100
 
100
101
  let requester
101
102
  let fspiopError
102
-
103
+ let step
103
104
  try {
104
105
  requester = await validateRequester({ source, proxy, proxyCache })
105
106
 
@@ -114,8 +115,10 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
114
115
  // the requester has specified a destination routing header. We should respect that and forward the request directly to the destination
115
116
  // without consulting any oracles.
116
117
  if (destination) {
118
+ step = 'validateParticipant-1'
117
119
  const destParticipantModel = await participant.validateParticipant(destination)
118
120
  if (!destParticipantModel) {
121
+ step = 'lookupProxyByDfspId-2'
119
122
  const proxyId = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
120
123
 
121
124
  if (!proxyId) {
@@ -125,6 +128,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
125
128
  destination = proxyId
126
129
  }
127
130
  // all ok, go ahead and forward the request
131
+ step = 'sendRequest-3'
128
132
  await participant.sendRequest(headers, destination, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
129
133
 
130
134
  histTimerEnd({ success: true })
@@ -132,6 +136,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
132
136
  return
133
137
  }
134
138
 
139
+ step = 'oracleRequest-4'
135
140
  const response = await oracle.oracleRequest(headers, method, params, query, undefined, cache)
136
141
  if (Array.isArray(response?.data?.partyList) && response.data.partyList.length > 0) {
137
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:
@@ -159,6 +164,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
159
164
  if (!destination) {
160
165
  clonedHeaders[Headers.FSPIOP.DESTINATION] = fspId
161
166
  }
167
+ step = 'validateParticipant-5'
162
168
  const schemeParticipant = await participant.validateParticipant(fspId)
163
169
  if (schemeParticipant) {
164
170
  sentCount++
@@ -169,6 +175,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
169
175
  // If the participant is not in the scheme and proxy routing is enabled,
170
176
  // we should check if there is a proxy for it and send the request to the proxy
171
177
  if (proxyEnabled) {
178
+ step = 'lookupProxyByDfspId-6'
172
179
  const proxyName = await proxyCache.lookupProxyByDfspId(fspId)
173
180
  if (!proxyName) {
174
181
  log.warn('no proxyMapping for participant! TODO: Delete reference in oracle...', { fspId })
@@ -180,6 +187,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
180
187
  }
181
188
  }
182
189
  })
190
+ step = 'sendRequests-7'
183
191
  await Promise.all(sending)
184
192
  log.info('participant.sendRequests to filtered oracle partyList are done', { sentCount })
185
193
  // todo: think what if sentCount === 0 here
@@ -188,6 +196,7 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
188
196
  let filteredProxyNames = []
189
197
 
190
198
  if (proxyEnabled) {
199
+ step = 'getAllProxiesNames-8'
191
200
  const proxyNames = await Util.proxies.getAllProxiesNames(Config.SWITCH_ENDPOINT)
192
201
  filteredProxyNames = proxyNames.filter(name => name !== proxy)
193
202
  }
@@ -203,16 +212,19 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
203
212
  })
204
213
  fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
205
214
  const errorCallbackEndpointType = utils.errorPartyCbType(partySubId)
215
+ step = 'sendErrorToParticipant-9'
206
216
  await participant.sendErrorToParticipant(requester, errorCallbackEndpointType,
207
217
  fspiopError.toApiErrorObject(config.ERROR_HANDLING), callbackHeaders, params, childSpan)
208
218
  } else {
209
219
  const alsReq = utils.alsRequestDto(source, params)
210
220
  log.info('starting setSendToProxiesList flow: ', { filteredProxyNames, alsReq, proxyCacheTtlSec })
221
+ step = 'setSendToProxiesList-10'
211
222
  const isCached = await proxyCache.setSendToProxiesList(alsReq, filteredProxyNames, proxyCacheTtlSec)
212
223
  if (!isCached) {
213
224
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, ERROR_MESSAGES.failedToCacheSendToProxiesList)
214
225
  }
215
226
 
227
+ step = 'sending-11'
216
228
  const sending = filteredProxyNames.map(
217
229
  proxyName => participant.sendRequest(headers, proxyName, callbackEndpointType, RestMethods.GET, undefined, options, childSpan)
218
230
  .then(({ status, data } = {}) => ({ status, data }))
@@ -232,6 +244,14 @@ const getPartiesByTypeAndID = async (headers, params, method, query, span, cache
232
244
  } catch (err) {
233
245
  fspiopError = await handleErrorOnSendingCallback(err, headers, params, requester)
234
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
+ })
235
255
  } finally {
236
256
  await utils.finishSpanWithError(childSpan, fspiopError)
237
257
  }
@@ -65,6 +65,7 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
65
65
  'Put parties by type and id',
66
66
  ['success']
67
67
  ).startTimer()
68
+ const errorCounter = Metrics.getCounter('errorCount')
68
69
  const type = params.Type
69
70
  const partySubId = params.SubId
70
71
  const source = headers[Headers.FSPIOP.SOURCE]
@@ -74,13 +75,16 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
74
75
  log.info('parties::putPartiesByTypeAndID::begin', { source, destination, proxy, params })
75
76
 
76
77
  let sendTo
78
+ let step
77
79
  try {
80
+ step = 'validateParticipant-1'
78
81
  const requesterParticipant = await participant.validateParticipant(source)
79
82
  if (!requesterParticipant) {
80
83
  if (!proxyEnabled || !proxy) {
81
84
  const errMessage = ERROR_MESSAGES.partySourceFspNotFound
82
85
  throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, errMessage)
83
86
  }
87
+ step = 'addDfspIdToProxyMapping-1'
84
88
  const isCached = await proxyCache.addDfspIdToProxyMapping(source, proxy)
85
89
  // think,if we should throw error if isCached === false?
86
90
  log.info('addDfspIdToProxyMapping is done', { source, proxy, isCached })
@@ -88,6 +92,7 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
88
92
 
89
93
  if (proxyEnabled && proxy) {
90
94
  const alsReq = utils.alsRequestDto(destination, params) // or source?
95
+ step = 'receivedSuccessResponse-2'
91
96
  const isExists = await proxyCache.receivedSuccessResponse(alsReq)
92
97
  if (!isExists) {
93
98
  log.warn('destination is NOT in scheme, and no cached sendToProxiesList', { destination, alsReq })
@@ -96,13 +101,15 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
96
101
  const mappingPayload = {
97
102
  fspId: source
98
103
  }
104
+ step = 'oracleRequest-3'
99
105
  await oracle.oracleRequest(headers, RestMethods.POST, params, null, mappingPayload, cache)
100
106
  log.info('oracle was updated with mappingPayload', { mappingPayload, params })
101
107
  }
102
108
  }
103
-
109
+ step = 'validateParticipant-4'
104
110
  const destinationParticipant = await participant.validateParticipant(destination)
105
111
  if (!destinationParticipant) {
112
+ step = 'lookupProxyByDfspId-5'
106
113
  const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
107
114
  if (!proxyName) {
108
115
  const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
@@ -120,12 +127,21 @@ const putPartiesByTypeAndID = async (headers, params, method, payload, dataUri,
120
127
  partyIdentifier: params.ID,
121
128
  ...(partySubId && { partySubIdOrType: partySubId })
122
129
  }
130
+ step = 'sendRequest-6'
123
131
  await participant.sendRequest(headers, sendTo, callbackEndpointType, RestMethods.PUT, decodedPayload.body.toString(), options)
124
132
 
125
133
  log.info('parties::putPartiesByTypeAndID::callback was sent', { sendTo, options })
126
134
  histTimerEnd({ success: true })
127
135
  } catch (err) {
128
- await handleErrorOnSendingCallback(err, headers, params, sendTo)
136
+ const fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
137
+ const extensions = err.extensions || []
138
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
139
+ errorCounter.inc({
140
+ code: fspiopError?.apiErrorCode,
141
+ system,
142
+ operation: 'putPartiesByTypeAndID',
143
+ step
144
+ })
129
145
  histTimerEnd({ success: false })
130
146
  }
131
147
  }
@@ -149,6 +165,7 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
149
165
  'Put parties error by type and id',
150
166
  ['success']
151
167
  ).startTimer()
168
+ const errorCounter = Metrics.getCounter('errorCount')
152
169
  const partySubId = params.SubId
153
170
  const destination = headers[Headers.FSPIOP.DESTINATION]
154
171
  const callbackEndpointType = utils.errorPartyCbType(partySubId)
@@ -158,12 +175,14 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
158
175
 
159
176
  let sendTo
160
177
  let fspiopError
178
+ let step
161
179
 
162
180
  try {
163
181
  const proxy = proxyEnabled && headers[Headers.FSPIOP.PROXY]
164
182
  if (proxy) {
165
183
  if (isNotValidPayeeCase(payload)) {
166
184
  const swappedHeaders = utils.swapSourceDestinationHeaders(headers)
185
+ step = 'oracleRequest-1'
167
186
  await oracle.oracleRequest(swappedHeaders, RestMethods.DELETE, params, null, null, cache)
168
187
  getPartiesByTypeAndID(swappedHeaders, params, RestMethods.GET, {}, span, cache, proxyCache)
169
188
  // todo: - think if we need to send errorCallback?
@@ -173,18 +192,20 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
173
192
  }
174
193
 
175
194
  const alsReq = utils.alsRequestDto(destination, params) // or source?
195
+ step = 'receivedErrorResponse-2'
176
196
  const isLast = await proxyCache.receivedErrorResponse(alsReq, proxy)
177
197
  if (!isLast) {
178
198
  log.info('got NOT last error callback from proxy:', { proxy, alsReq })
179
199
  return
180
200
  }
181
201
  }
182
-
202
+ step = 'validateParticipant-3'
183
203
  const destinationParticipant = await participant.validateParticipant(destination)
184
204
 
185
205
  if (destinationParticipant) {
186
206
  sendTo = destination
187
207
  } else {
208
+ step = 'lookupProxyByDfspId-4'
188
209
  const proxyName = proxyEnabled && await proxyCache.lookupProxyByDfspId(destination)
189
210
  if (!proxyName) {
190
211
  const errMessage = ERROR_MESSAGES.partyDestinationFspNotFound
@@ -199,6 +220,14 @@ const putPartiesErrorByTypeAndID = async (headers, params, payload, dataUri, spa
199
220
  histTimerEnd({ success: true })
200
221
  } catch (err) {
201
222
  fspiopError = await handleErrorOnSendingCallback(err, headers, params, sendTo)
223
+ const extensions = err.extensions || []
224
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
225
+ errorCounter.inc({
226
+ code: fspiopError?.apiErrorCode,
227
+ system,
228
+ operation: 'putPartiesErrorByTypeAndID',
229
+ step
230
+ })
202
231
  histTimerEnd({ success: false })
203
232
  } finally {
204
233
  await utils.finishSpanWithError(childSpan, fspiopError)
@@ -57,20 +57,31 @@ const sendTimeoutCallback = async (cacheKey) => {
57
57
  'Egress - Interscheme parties lookup timeout callback',
58
58
  ['success']
59
59
  ).startTimer()
60
-
60
+ const errorCounter = Metrics.getCounter('errorCount')
61
+ let step
61
62
  const [, destination, partyType, partyId] = cacheKey.split(':')
62
63
  const { errorInformation, params, headers, endpointType, span } = await timeoutCallbackDto({ destination, partyId, partyType })
63
64
  logger.debug('sendTimeoutCallback details:', { destination, partyType, partyId, cacheKey })
64
65
 
65
66
  try {
67
+ step = 'validateParticipant-1'
66
68
  await validateParticipant(destination)
67
69
  await span.audit({ headers, errorInformation }, AuditEventAction.start)
70
+ step = 'sendErrorToParticipant-2'
68
71
  await Participant.sendErrorToParticipant(destination, endpointType, errorInformation, headers, params, undefined, span)
69
72
  histTimerEnd({ success: true })
70
73
  } catch (err) {
71
74
  logger.warn('error in sendTimeoutCallback: ', err)
72
75
  histTimerEnd({ success: false })
73
76
  const fspiopError = reformatFSPIOPError(err)
77
+ const extensions = err.extensions || []
78
+ const system = extensions.find((element) => element.key === 'system')?.value || ''
79
+ errorCounter.inc({
80
+ code: fspiopError?.apiErrorCode,
81
+ system,
82
+ operation: 'sendTimeoutCallback',
83
+ step
84
+ })
74
85
  await finishSpan(span, fspiopError)
75
86
  throw fspiopError
76
87
  }
@@ -39,7 +39,16 @@ const getCurrencyById = async (currencyId) => {
39
39
  try {
40
40
  return Db.from('currency').findOne({ currencyId, isActive: true })
41
41
  } catch (err) {
42
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
42
+ const extensions = [{
43
+ key: 'system',
44
+ value: '["db"]'
45
+ }]
46
+ throw ErrorHandler.Factory.reformatFSPIOPError(
47
+ err,
48
+ undefined,
49
+ undefined,
50
+ extensions
51
+ )
43
52
  }
44
53
  }
45
54
 
@@ -39,7 +39,16 @@ const getEndpointTypeByType = async (type) => {
39
39
  try {
40
40
  return Db.from('endpointType').findOne({ type, isActive: true })
41
41
  } catch (err) {
42
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
42
+ const extensions = [{
43
+ key: 'system',
44
+ value: '["db"]'
45
+ }]
46
+ throw ErrorHandler.Factory.reformatFSPIOPError(
47
+ err,
48
+ undefined,
49
+ undefined,
50
+ extensions
51
+ )
43
52
  }
44
53
  }
45
54
 
@@ -123,6 +123,10 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
123
123
  throw err
124
124
  }
125
125
  } catch (err) {
126
+ const extensions = [{
127
+ key: 'system',
128
+ value: '["@hapi/catbox-memory","http"]'
129
+ }]
126
130
  Logger.isErrorEnabled && Logger.error(`error in oracleRequest: ${err?.stack}`)
127
131
  // If the error was a 400 from the Oracle, we'll modify the error to generate a response to the
128
132
  // initiator of the request.
@@ -131,15 +135,32 @@ exports.oracleRequest = async (headers, method, params = {}, query = {}, payload
131
135
  err.apiErrorCode.code === ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_COMMUNICATION_ERROR.code
132
136
  ) {
133
137
  if (err.extensions.some(ext => (ext.key === 'status' && ext.value === Enums.Http.ReturnCodes.BADREQUEST.CODE))) {
134
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND)
138
+ throw ErrorHandler.Factory.createFSPIOPError(
139
+ ErrorHandler.Enums.FSPIOPErrorCodes.PARTY_NOT_FOUND,
140
+ undefined,
141
+ undefined,
142
+ undefined,
143
+ extensions
144
+ )
135
145
  // Added error 404 to cover a special case of the Mowali implementation
136
146
  // which uses mojaloop/als-oracle-pathfinder and currently returns 404
137
147
  // and in which case the Mowali implementation expects back `DESTINATION_FSP_ERROR`.
138
148
  } else if (err.extensions.some(ext => (ext.key === 'status' && ext.value === Enums.Http.ReturnCodes.NOTFOUND.CODE))) {
139
- throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR)
149
+ throw ErrorHandler.Factory.createFSPIOPError(
150
+ ErrorHandler.Enums.FSPIOPErrorCodes.DESTINATION_FSP_ERROR,
151
+ undefined,
152
+ undefined,
153
+ undefined,
154
+ extensions
155
+ )
140
156
  }
141
157
  }
142
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
158
+ throw ErrorHandler.Factory.reformatFSPIOPError(
159
+ err,
160
+ undefined,
161
+ undefined,
162
+ extensions
163
+ )
143
164
  }
144
165
  }
145
166
 
@@ -27,6 +27,10 @@
27
27
 
28
28
  const Db = require('../../lib/db')
29
29
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
30
+ const extensions = [{
31
+ key: 'system',
32
+ value: '["db"]'
33
+ }]
30
34
 
31
35
  const getOracleEndpointByType = async (type) => {
32
36
  try {
@@ -43,7 +47,12 @@ const getOracleEndpointByType = async (type) => {
43
47
  'pt.name as idType', 'oracleEndpoint.currencyId as currency', 'oracleEndpoint.isDefault')
44
48
  })
45
49
  } catch (err) {
46
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
50
+ throw ErrorHandler.Factory.reformatFSPIOPError(
51
+ err,
52
+ undefined,
53
+ undefined,
54
+ extensions
55
+ )
47
56
  }
48
57
  }
49
58
 
@@ -64,7 +73,12 @@ const getOracleEndpointByTypeAndCurrency = async (type, currencyId) => {
64
73
  'pt.name as idType', 'oracleEndpoint.currencyId as currency', 'oracleEndpoint.isDefault')
65
74
  })
66
75
  } catch (err) {
67
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
76
+ throw ErrorHandler.Factory.reformatFSPIOPError(
77
+ err,
78
+ undefined,
79
+ undefined,
80
+ extensions
81
+ )
68
82
  }
69
83
  }
70
84
 
@@ -84,7 +98,12 @@ const getOracleEndpointByCurrency = async (currencyId) => {
84
98
  'pt.name as idType', 'oracleEndpoint.currencyId as currency', 'oracleEndpoint.isDefault')
85
99
  })
86
100
  } catch (err) {
87
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
101
+ throw ErrorHandler.Factory.reformatFSPIOPError(
102
+ err,
103
+ undefined,
104
+ undefined,
105
+ extensions
106
+ )
88
107
  }
89
108
  }
90
109
 
@@ -104,7 +123,12 @@ const getOracleEndpointById = async (oracleEndpointId) => {
104
123
  'pt.name as idType', 'oracleEndpoint.currencyId as currency', 'oracleEndpoint.isDefault')
105
124
  })
106
125
  } catch (err) {
107
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
126
+ throw ErrorHandler.Factory.reformatFSPIOPError(
127
+ err,
128
+ undefined,
129
+ undefined,
130
+ extensions
131
+ )
108
132
  }
109
133
  }
110
134
 
@@ -122,7 +146,12 @@ const getAllOracleEndpoint = async () => {
122
146
  'pt.name as idType', 'oracleEndpoint.currencyId as currency', 'oracleEndpoint.isDefault')
123
147
  })
124
148
  } catch (err) {
125
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
149
+ throw ErrorHandler.Factory.reformatFSPIOPError(
150
+ err,
151
+ undefined,
152
+ undefined,
153
+ extensions
154
+ )
126
155
  }
127
156
  }
128
157
 
@@ -142,7 +171,12 @@ const getAllOracleEndpointsByMatchCondition = async (oracleEndpointModel, partyI
142
171
  'oracleEndpoint.isActive', 'oracleEndpoint.partyIdTypeId', 'oracleEndpoint.endpointTypeId', 'oracleEndpoint.currencyId')
143
172
  })
144
173
  } catch (err) {
145
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
174
+ throw ErrorHandler.Factory.reformatFSPIOPError(
175
+ err,
176
+ undefined,
177
+ undefined,
178
+ extensions
179
+ )
146
180
  }
147
181
  }
148
182
 
@@ -150,7 +184,12 @@ const createOracleEndpoint = async (oracleEndpointModel) => {
150
184
  try {
151
185
  return await Db.from('oracleEndpoint').insert(oracleEndpointModel)
152
186
  } catch (err) {
153
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
187
+ throw ErrorHandler.Factory.reformatFSPIOPError(
188
+ err,
189
+ undefined,
190
+ undefined,
191
+ extensions
192
+ )
154
193
  }
155
194
  }
156
195
 
@@ -158,7 +197,12 @@ const updateOracleEndpointById = async (id, oracleEndpointModel) => {
158
197
  try {
159
198
  return await Db.from('oracleEndpoint').update({ oracleEndpointId: id }, oracleEndpointModel)
160
199
  } catch (err) {
161
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
200
+ throw ErrorHandler.Factory.reformatFSPIOPError(
201
+ err,
202
+ undefined,
203
+ undefined,
204
+ extensions
205
+ )
162
206
  }
163
207
  }
164
208
 
@@ -166,7 +210,12 @@ const setIsActiveOracleEndpoint = async (oracleType, isActive) => {
166
210
  try {
167
211
  return await Db.from('oracleEndpoint').update({ oracleType }, { isActive })
168
212
  } catch (err) {
169
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
213
+ throw ErrorHandler.Factory.reformatFSPIOPError(
214
+ err,
215
+ undefined,
216
+ undefined,
217
+ extensions
218
+ )
170
219
  }
171
220
  }
172
221
 
@@ -174,7 +223,12 @@ const destroyOracleEndpointById = async (oracleEndpointId) => {
174
223
  try {
175
224
  return await Db.from('oracleEndpoint').update({ oracleEndpointId }, { isActive: false })
176
225
  } catch (err) {
177
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
226
+ throw ErrorHandler.Factory.reformatFSPIOPError(
227
+ err,
228
+ undefined,
229
+ undefined,
230
+ extensions
231
+ )
178
232
  }
179
233
  }
180
234
 
@@ -31,6 +31,10 @@ const Metrics = require('@mojaloop/central-services-metrics')
31
31
  const OracleEndpointUncached = require('./oracleEndpoint')
32
32
 
33
33
  let cacheClient
34
+ const extensions = [{
35
+ key: 'system',
36
+ value: '["db","@hapi/catbox-memory"]'
37
+ }]
34
38
 
35
39
  const getCacheKey = (params) => {
36
40
  return cacheClient.createKey(`${Object.values(params).join('__')}`)
@@ -87,7 +91,12 @@ exports.getOracleEndpointByTypeAndCurrency = async (partyIdType, currency) => {
87
91
  try {
88
92
  return await getOracleEndpointCached({ partyIdType, currency })
89
93
  } catch (err) {
90
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
94
+ throw ErrorHandler.Factory.reformatFSPIOPError(
95
+ err,
96
+ undefined,
97
+ undefined,
98
+ extensions
99
+ )
91
100
  }
92
101
  }
93
102
 
@@ -95,7 +104,12 @@ exports.getOracleEndpointByType = async (partyIdType) => {
95
104
  try {
96
105
  return await getOracleEndpointCached({ partyIdType })
97
106
  } catch (err) {
98
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
107
+ throw ErrorHandler.Factory.reformatFSPIOPError(
108
+ err,
109
+ undefined,
110
+ undefined,
111
+ extensions
112
+ )
99
113
  }
100
114
  }
101
115
 
@@ -103,6 +117,11 @@ exports.getOracleEndpointByCurrency = async (currency) => {
103
117
  try {
104
118
  return await getOracleEndpointCached({ currency })
105
119
  } catch (err) {
106
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
120
+ throw ErrorHandler.Factory.reformatFSPIOPError(
121
+ err,
122
+ undefined,
123
+ undefined,
124
+ extensions
125
+ )
107
126
  }
108
127
  }
@@ -126,7 +126,16 @@ exports.sendRequest = async (headers, requestedParticipant, endpointType, method
126
126
  } catch (err) {
127
127
  histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName: requestedParticipant })
128
128
  logger.warn('error in sendRequest: ', err)
129
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
129
+ const extensions = [{
130
+ key: 'system',
131
+ value: '["http"]'
132
+ }]
133
+ throw ErrorHandler.Factory.reformatFSPIOPError(
134
+ err,
135
+ undefined,
136
+ undefined,
137
+ extensions
138
+ )
130
139
  }
131
140
  }
132
141
 
@@ -151,7 +160,16 @@ exports.validateParticipant = async (fsp) => {
151
160
  } catch (err) {
152
161
  histTimerEnd({ success: false })
153
162
  logger.warn('error in validateParticipant: ', err)
154
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
163
+ const extensions = [{
164
+ key: 'system',
165
+ value: '["http"]'
166
+ }]
167
+ throw ErrorHandler.Factory.reformatFSPIOPError(
168
+ err,
169
+ undefined,
170
+ undefined,
171
+ extensions
172
+ )
155
173
  }
156
174
  }
157
175
 
@@ -191,7 +209,16 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo
191
209
  } catch (err) {
192
210
  histTimerEndGetParticipantEndpoint({ success: false, endpointType, participantName })
193
211
  logger.warn('error in getEndpoint: ', err)
194
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
212
+ const extensions = [{
213
+ key: 'system',
214
+ value: '["http"]'
215
+ }]
216
+ throw ErrorHandler.Factory.reformatFSPIOPError(
217
+ err,
218
+ undefined,
219
+ undefined,
220
+ extensions
221
+ )
195
222
  }
196
223
 
197
224
  // Send error to participant
@@ -235,6 +262,15 @@ exports.sendErrorToParticipant = async (participantName, endpointType, errorInfo
235
262
  } catch (err) {
236
263
  histTimerEndSendRequestToParticipant({ success: false, endpointType, participantName })
237
264
  logger.warn('error in sendErrorToParticipant: ', err)
238
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
265
+ const extensions = [{
266
+ key: 'system',
267
+ value: '["http"]'
268
+ }]
269
+ throw ErrorHandler.Factory.reformatFSPIOPError(
270
+ err,
271
+ undefined,
272
+ undefined,
273
+ extensions
274
+ )
239
275
  }
240
276
  }
@@ -32,7 +32,16 @@ const getPartyIdTypeByName = async (name) => {
32
32
  try {
33
33
  return Db.from('partyIdType').findOne({ name, isActive: true })
34
34
  } catch (err) {
35
- throw ErrorHandler.Factory.reformatFSPIOPError(err)
35
+ const extensions = [{
36
+ key: 'system',
37
+ value: '["db"]'
38
+ }]
39
+ throw ErrorHandler.Factory.reformatFSPIOPError(
40
+ err,
41
+ undefined,
42
+ undefined,
43
+ extensions
44
+ )
36
45
  }
37
46
  }
38
47
 
package/src/server.js CHANGED
@@ -105,6 +105,14 @@ const createServer = async (port, api, routes, isAdmin, proxyCacheConfig, proxyM
105
105
  await Plugins.registerPlugins(server, api, isAdmin)
106
106
 
107
107
  server.route(routes)
108
+
109
+ // Initialize the error count metric
110
+ Metrics.getCounter(
111
+ 'errorCount',
112
+ 'Error count',
113
+ ['code', 'system', 'operation', 'step']
114
+ )
115
+
108
116
  // TODO: follow instructions https://github.com/anttiviljami/openapi-backend/blob/master/DOCS.md#postresponsehandler-handler
109
117
  await server.start()
110
118
 
@@ -37,6 +37,7 @@ const partyIdType = require('../../../../src/models/partyIdType')
37
37
  const Db = require('../../../../src/lib/db')
38
38
  const oracleEndpointCached = require('../../../../src/models/oracle/oracleEndpointCached')
39
39
  const Logger = require('@mojaloop/central-services-logger')
40
+ const Metrics = require('@mojaloop/central-services-metrics')
40
41
 
41
42
  Logger.isDebugEnabled = jest.fn(() => true)
42
43
  Logger.isErrorEnabled = jest.fn(() => true)
@@ -90,6 +91,13 @@ let sandbox
90
91
  let SpanStub
91
92
 
92
93
  describe('Oracle tests', () => {
94
+ // Initialize Metrics for testing
95
+ Metrics.getCounter(
96
+ 'errorCount',
97
+ 'Error count',
98
+ ['code', 'system', 'operation', 'step']
99
+ )
100
+
93
101
  beforeEach(() => {
94
102
  sandbox = Sinon.createSandbox()
95
103
  Db.partyIdType = {
@@ -34,6 +34,7 @@ const Sinon = require('sinon')
34
34
  const Enums = require('@mojaloop/central-services-shared').Enum
35
35
  const Logger = require('@mojaloop/central-services-logger')
36
36
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
37
+ const Metrics = require('@mojaloop/central-services-metrics')
37
38
 
38
39
  const participantsDomain = require('../../../../src/domain/participants/participants')
39
40
  const participant = require('../../../../src/models/participantEndpoint/facade')
@@ -49,6 +50,12 @@ const { Headers } = Enums.Http
49
50
  describe('participant Tests', () => {
50
51
  describe('getParticipantsByTypeAndID', () => {
51
52
  let sandbox
53
+ // Initialize Metrics for testing
54
+ Metrics.getCounter(
55
+ 'errorCount',
56
+ 'Error count',
57
+ ['code', 'system', 'operation', 'step']
58
+ )
52
59
 
53
60
  beforeEach(() => {
54
61
  sandbox = Sinon.createSandbox()
@@ -37,6 +37,7 @@ const { createProxyCache } = require('@mojaloop/inter-scheme-proxy-cache-lib')
37
37
  const { Enum, Util } = require('@mojaloop/central-services-shared')
38
38
  const { MojaloopApiErrorCodes } = require('@mojaloop/sdk-standard-components').Errors
39
39
  const Logger = require('@mojaloop/central-services-logger')
40
+ const Metrics = require('@mojaloop/central-services-metrics')
40
41
 
41
42
  const Config = require('../../../../src/lib/config')
42
43
  const Db = require('../../../../src/lib/db')
@@ -65,6 +66,13 @@ let sandbox
65
66
  describe('Parties Tests', () => {
66
67
  let proxyCache
67
68
 
69
+ // Initialize Metrics for testing
70
+ Metrics.getCounter(
71
+ 'errorCount',
72
+ 'Error count',
73
+ ['code', 'system', 'operation', 'step']
74
+ )
75
+
68
76
  beforeEach(async () => {
69
77
  await Util.Endpoints.initializeCache(Config.CENTRAL_SHARED_ENDPOINT_CACHE_CONFIG, libUtil.hubNameConfig)
70
78
  sandbox = Sinon.createSandbox()
@@ -33,8 +33,16 @@
33
33
 
34
34
  const Participant = require('../../../../src/models/participantEndpoint/facade')
35
35
  const TimeoutDomain = require('../../../../src/domain/timeout')
36
+ const Metrics = require('@mojaloop/central-services-metrics')
36
37
 
37
38
  describe('Timeout Domain', () => {
39
+ // Initialize Metrics for testing
40
+ Metrics.getCounter(
41
+ 'errorCount',
42
+ 'Error count',
43
+ ['code', 'system', 'operation', 'step']
44
+ )
45
+
38
46
  beforeEach(() => {
39
47
  jest.clearAllMocks()
40
48
  jest.spyOn(Participant, 'validateParticipant').mockResolvedValue({})