@mojaloop/central-ledger 9.2.2 → 15.1.3-snapshot.2

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 (256) hide show
  1. package/.circleci/config.yml +625 -204
  2. package/.env +3 -2
  3. package/.ncurc.yaml +9 -0
  4. package/.nvmrc +1 -1
  5. package/.versionrc +15 -0
  6. package/CHANGELOG.md +353 -0
  7. package/CODEOWNERS +31 -0
  8. package/Dockerfile +20 -14
  9. package/LICENSE.md +4 -4
  10. package/Onboarding.md +0 -7
  11. package/README.md +99 -22
  12. package/audit-resolve.json +136 -1
  13. package/config/default.json +82 -27
  14. package/docker/central-ledger/default.json +68 -21
  15. package/docker/ml-api-adapter/default.json +10 -1
  16. package/docker/wait-for/wait-for-objstore.sh +1 -6
  17. package/docker/wait-for/wait-for.env +1 -1
  18. package/docker-compose.integration.yml +8 -5
  19. package/docker-compose.yml +73 -16
  20. package/migrations/310203_transferParticipant-indexes.js +38 -0
  21. package/migrations/310503_participantLimit-indexes-composite.js +38 -0
  22. package/migrations/{400401_settlementWindowStateChange-indexes.js → 400401_settlmentWindowStateChange-indexes.js} +0 -0
  23. package/migrations/401301_settlementModel-indexes.js +2 -0
  24. package/migrations/500401_quote-hotfix-2719.js +58 -0
  25. package/migrations/500501_feature-fixSubIdRef.js +53 -0
  26. package/migrations/500601_party-2480.js +63 -0
  27. package/migrations/501001_quoteExtension-quote-service-174.js +46 -0
  28. package/migrations/501002_quoteExtension-2522.js +61 -0
  29. package/migrations/800101_feature-fixSubIdRef.js +90 -0
  30. package/migrations/910102_hotfix954.js +4 -1
  31. package/migrations/940100_quotePartyIdInfoExtension.js +52 -0
  32. package/migrations/940101_quotePartyIdInfoExtension-2522.js +64 -0
  33. package/migrations/950100_settlementContentAggregation-decimal184.js +40 -0
  34. package/migrations/950101_transferParticipantStateChange.js +46 -0
  35. package/migrations/950102_settlementModel-adjustPosition.js +42 -0
  36. package/migrations/950103_dropTransferParticipantStateChange.js +47 -0
  37. package/migrations/950104_settlementModel-settlementAccountTypeId.js +79 -0
  38. package/migrations/950106_unique_settlement_model_ledger_account_currency.js +38 -0
  39. package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +60 -0
  40. package/package.json +99 -81
  41. package/scripts/_wait4_all.js +146 -0
  42. package/seeds/amountType.js +4 -6
  43. package/seeds/balanceOfPayments.js +4 -6
  44. package/seeds/bulkProcessingState.js +4 -6
  45. package/seeds/bulkTransferState.js +4 -6
  46. package/seeds/currency.js +4 -6
  47. package/seeds/endpointType.js +128 -7
  48. package/seeds/ledgerAccountType.js +11 -10
  49. package/seeds/ledgerEntryType.js +5 -7
  50. package/seeds/participant.js +4 -6
  51. package/seeds/participantLimitType.js +4 -6
  52. package/seeds/partyIdentifierType.js +4 -6
  53. package/seeds/partyType.js +4 -6
  54. package/seeds/settlementDelay.js +4 -6
  55. package/seeds/settlementGranularity.js +4 -6
  56. package/seeds/settlementInterchange.js +4 -6
  57. package/seeds/settlementState.js +4 -6
  58. package/seeds/settlementWindow1State.js +4 -6
  59. package/seeds/transactionInitiator.js +4 -6
  60. package/seeds/transactionInitiatorType.js +4 -6
  61. package/seeds/transactionScenario.js +4 -6
  62. package/seeds/transferParticipantRoleType.js +4 -6
  63. package/seeds/transferState.js +9 -6
  64. package/src/api/interface/swagger.json +728 -948
  65. package/src/api/ledgerAccountTypes/handler.js +55 -0
  66. package/src/api/ledgerAccountTypes/routes.js +63 -0
  67. package/src/api/metrics/handler.js +2 -2
  68. package/src/api/metrics/routes.js +1 -1
  69. package/src/api/participants/handler.js +43 -35
  70. package/src/api/participants/routes.js +13 -13
  71. package/src/api/root/routes.js +2 -2
  72. package/src/api/settlementModels/handler.js +9 -26
  73. package/src/api/settlementModels/routes.js +4 -4
  74. package/src/api/transactions/handler.js +46 -0
  75. package/src/api/transactions/routes.js +45 -0
  76. package/src/domain/bulkTransfer/index.js +6 -5
  77. package/src/domain/ledgerAccountTypes/index.js +58 -0
  78. package/src/domain/participant/index.js +55 -4
  79. package/src/domain/position/index.js +18 -2
  80. package/src/domain/settlement/index.js +39 -6
  81. package/src/domain/transactions/index.js +61 -0
  82. package/src/domain/transfer/index.js +22 -2
  83. package/src/domain/transfer/transform.js +3 -3
  84. package/src/handlers/admin/handler.js +24 -32
  85. package/src/handlers/bulk/fulfil/handler.js +89 -56
  86. package/src/handlers/bulk/get/handler.js +203 -0
  87. package/src/handlers/bulk/index.js +4 -1
  88. package/src/handlers/bulk/prepare/handler.js +30 -29
  89. package/src/handlers/bulk/processing/handler.js +61 -32
  90. package/src/handlers/bulk/shared/validator.js +13 -1
  91. package/src/handlers/index.js +18 -9
  92. package/src/handlers/positions/handler.js +67 -46
  93. package/src/handlers/register.js +7 -7
  94. package/src/handlers/timeouts/handler.js +9 -4
  95. package/src/handlers/transfers/handler.js +408 -182
  96. package/src/handlers/transfers/validator.js +41 -11
  97. package/src/lib/cache.js +8 -34
  98. package/src/lib/config.js +5 -7
  99. package/src/lib/enum.js +22 -22
  100. package/src/lib/enumCached.js +76 -0
  101. package/src/lib/healthCheck/subServiceHealth.js +2 -2
  102. package/src/lib/requestLogger.js +9 -9
  103. package/src/lib/urlParser.js +1 -1
  104. package/src/models/bulkTransfer/bulkTransfer.js +6 -6
  105. package/src/models/bulkTransfer/bulkTransferAssociation.js +3 -3
  106. package/src/models/bulkTransfer/bulkTransferDuplicateCheck.js +4 -4
  107. package/src/models/bulkTransfer/bulkTransferExtension.js +2 -2
  108. package/src/models/bulkTransfer/bulkTransferFulfilmentDuplicateCheck.js +4 -4
  109. package/src/models/bulkTransfer/bulkTransferStateChange.js +7 -7
  110. package/src/models/bulkTransfer/facade.js +54 -3
  111. package/src/models/bulkTransfer/individualTransfer.js +2 -2
  112. package/src/models/ilpPackets/ilpPacket.js +36 -0
  113. package/src/models/ledgerAccountType/ledgerAccountType.js +139 -3
  114. package/src/models/misc/migrationLock.js +1 -1
  115. package/src/models/misc/segment.js +1 -1
  116. package/src/models/participant/facade.js +226 -76
  117. package/src/models/participant/participant.js +6 -5
  118. package/src/models/participant/participantCached.js +8 -0
  119. package/src/models/participant/participantCurrency.js +7 -7
  120. package/src/models/participant/participantCurrencyCached.js +11 -1
  121. package/src/models/participant/participantLimit.js +13 -13
  122. package/src/models/participant/participantLimitCached.js +124 -0
  123. package/src/models/participant/participantPosition.js +34 -5
  124. package/src/models/participant/participantPositionChange.js +10 -2
  125. package/src/models/position/facade.js +112 -18
  126. package/src/models/position/participantPosition.js +6 -6
  127. package/src/models/settlement/settlementModel.js +92 -17
  128. package/src/models/settlement/settlementModelCached.js +139 -0
  129. package/src/models/transfer/facade.js +97 -32
  130. package/src/models/transfer/ilpPacket.js +4 -4
  131. package/src/models/transfer/transfer.js +7 -7
  132. package/src/models/transfer/transferDuplicateCheck.js +21 -5
  133. package/src/models/transfer/transferError.js +5 -5
  134. package/src/models/transfer/transferErrorDuplicateCheck.js +4 -4
  135. package/src/models/transfer/transferExtension.js +4 -4
  136. package/src/models/transfer/transferFulfilment.js +5 -5
  137. package/src/models/transfer/transferFulfilmentDuplicateCheck.js +4 -4
  138. package/src/models/transfer/transferParticipant.js +3 -3
  139. package/src/models/transfer/transferStateChange.js +11 -11
  140. package/src/models/transfer/transferTimeout.js +5 -5
  141. package/src/schema/bulkTransfer.js +4 -4
  142. package/src/shared/plugins.js +10 -9
  143. package/src/shared/setup.js +69 -46
  144. package/test/integration/domain/participant/index.test.js +2 -0
  145. package/test/integration/handlers/handlers.test.js +605 -166
  146. package/test/integration/handlers/root.test.js +1 -1
  147. package/test/integration/helpers/hubAccounts.js +62 -0
  148. package/test/integration/helpers/ilpPacket.js +1 -1
  149. package/test/integration/helpers/participantFundsInOut.js +80 -0
  150. package/test/integration/helpers/participantLimit.js +6 -1
  151. package/test/integration/helpers/settlementModels.js +88 -0
  152. package/test/integration/helpers/testConsumer.js +163 -0
  153. package/test/integration/helpers/testProducer.js +0 -1
  154. package/test/integration/helpers/transferDuplicateCheck.js +1 -1
  155. package/test/integration/helpers/transferStateChange.js +1 -1
  156. package/test/integration/helpers/transferTestHelper.js +3 -5
  157. package/test/integration/models/transfer/transferStateChange.test.js +6 -0
  158. package/test/unit/api/ledgerAccountTypes/handler.test.js +126 -0
  159. package/test/unit/api/ledgerAccountTypes/routes.test.js +52 -0
  160. package/test/unit/api/participants/handler.test.js +75 -33
  161. package/test/unit/api/root/handler.test.js +1 -1
  162. package/test/unit/api/root/routes.test.js +2 -2
  163. package/test/unit/api/settlementModels/handler.test.js +21 -57
  164. package/test/unit/api/transactions/handler.test.js +108 -0
  165. package/test/unit/api/transactions/routes.test.js +38 -0
  166. package/test/unit/domain/ledgerAccountTypes/index.test.js +197 -0
  167. package/test/unit/domain/participant/index.test.js +115 -2
  168. package/test/unit/domain/settlement/index.test.js +199 -29
  169. package/test/unit/domain/transactions/index.test.js +128 -0
  170. package/test/unit/domain/transfer/index.test.js +1 -0
  171. package/test/unit/domain/transfer/transform.test.js +2 -0
  172. package/test/unit/handlers/admin/handler.test.js +5 -5
  173. package/test/unit/handlers/bulk/get/handler.test.js +401 -0
  174. package/test/unit/handlers/index.test.js +6 -0
  175. package/test/unit/handlers/positions/handler.test.js +26 -0
  176. package/test/unit/handlers/register.test.js +2 -0
  177. package/test/unit/handlers/transfers/handler.test.js +491 -36
  178. package/test/unit/handlers/transfers/validator.test.js +31 -31
  179. package/test/unit/lib/cache.test.js +0 -64
  180. package/test/unit/lib/cachingOfEnums.test.js +121 -0
  181. package/test/unit/lib/config.test.js +34 -0
  182. package/test/unit/lib/enum.test.js +6 -1
  183. package/test/unit/lib/enumCached.test.js +82 -0
  184. package/test/unit/lib/healthCheck/subServiceHealth.test.js +2 -0
  185. package/test/unit/lib/requestLogger.test.js +14 -12
  186. package/test/unit/models/ilpPackets/ilpPacket.test.js +89 -0
  187. package/test/unit/models/ledgerAccountType/ledgerAccountType.test.js +255 -6
  188. package/test/unit/models/misc/migrationLock.test.js +4 -0
  189. package/test/unit/models/misc/segment.test.js +5 -0
  190. package/test/unit/models/participant/facade.test.js +493 -54
  191. package/test/unit/models/participant/participant.test.js +4 -0
  192. package/test/unit/models/participant/participantCurrency.test.js +22 -9
  193. package/test/unit/models/participant/participantCurrencyCached.test.js +1 -1
  194. package/test/unit/models/participant/participantLimit.test.js +97 -0
  195. package/test/unit/models/participant/participantLimitCached.test.js +232 -0
  196. package/test/unit/models/participant/participantPosition.test.js +217 -0
  197. package/test/unit/models/participant/participantPositionChange.test.js +3 -0
  198. package/test/unit/models/position/facade.test.js +318 -29
  199. package/test/unit/models/position/participantPosition.test.js +22 -7
  200. package/test/unit/models/settlement/settlementModel.test.js +18 -37
  201. package/test/unit/models/settlement/settlementModelCached.test.js +171 -0
  202. package/test/unit/models/transfer/facade.test.js +115 -0
  203. package/test/unit/models/transfer/ilpPacket.test.js +28 -11
  204. package/test/unit/models/transfer/transfer.test.js +13 -4
  205. package/test/unit/models/transfer/transferDuplicateCheck.test.js +4 -0
  206. package/test/unit/models/transfer/transferError.test.js +5 -1
  207. package/test/unit/models/transfer/transferErrorDuplicateCheck.test.js +4 -0
  208. package/test/unit/models/transfer/transferExtension.test.js +26 -9
  209. package/test/unit/models/transfer/transferFulfilment.test.js +14 -5
  210. package/test/unit/models/transfer/transferFulfilmentDuplicateCheck.test.js +4 -0
  211. package/test/unit/models/transfer/transferParticipant.test.js +14 -5
  212. package/test/unit/models/transfer/transferStateChange.test.js +3 -0
  213. package/test/unit/models/transfer/transferTimeout.test.js +6 -0
  214. package/test/unit/seeds/amountType.test.js +6 -22
  215. package/test/unit/seeds/balanceOfPayments.test.js +6 -22
  216. package/test/unit/seeds/bulkProcessingState.test.js +84 -0
  217. package/test/unit/{lib/sidecar/nullClient.test.js → seeds/bulkTransferState.test.js} +36 -39
  218. package/test/unit/seeds/currency.test.js +6 -22
  219. package/test/unit/seeds/endpointType.test.js +6 -22
  220. package/test/unit/seeds/ledgerAccountType.test.js +5 -22
  221. package/test/unit/seeds/ledgerEntryType.test.js +6 -22
  222. package/test/unit/seeds/participant.test.js +5 -22
  223. package/test/unit/seeds/participantLimitType.test.js +6 -22
  224. package/test/unit/seeds/partyIdentifierType.test.js +6 -22
  225. package/test/unit/seeds/partyType.test.js +6 -22
  226. package/test/unit/seeds/settlementState.test.js +5 -22
  227. package/test/unit/seeds/settlementWindow1State.test.js +5 -22
  228. package/test/unit/seeds/transactionInitiator.test.js +6 -22
  229. package/test/unit/seeds/transactionInitiatorType.test.js +6 -22
  230. package/test/unit/seeds/transactionScenario.test.js +6 -22
  231. package/test/unit/seeds/transferParticipantRoleType.test.js +6 -22
  232. package/test/unit/seeds/transferState.test.js +6 -22
  233. package/test/unit/shared/plugins.test.js +31 -1
  234. package/test/unit/shared/setup.test.js +25 -36
  235. package/test/util/helpers.js +37 -2
  236. package/test/util/randomTransfers.js +1 -1
  237. package/test/util/scripts/env.sh +6 -5
  238. package/test/util/scripts/populateTestData.sh +204 -181
  239. package/test/util/scripts/populateTestDataForLegacySimulator.sh +261 -0
  240. package/test/util/scripts/restartObjStore.sh +1 -1
  241. package/test-integration.Dockerfile +15 -15
  242. package/test.Dockerfile +12 -12
  243. package/.circleci/_set_up_deploy_envs.sh +0 -47
  244. package/.dockerignore +0 -17
  245. package/.ncurc.json +0 -3
  246. package/server.sh +0 -4
  247. package/sonar-project.properties +0 -17
  248. package/src/lib/sidecar/index.js +0 -47
  249. package/src/lib/sidecar/nullClient.js +0 -18
  250. package/test/.env +0 -8
  251. package/test/integration-config.json +0 -367
  252. package/test/integration-runner.env +0 -31
  253. package/test/integration-runner.sh +0 -264
  254. package/test/spec-runner.sh +0 -132
  255. package/test/unit/lib/sidecar/index.test.js +0 -148
  256. package/test/unit/models/position/participantLimit.test.js +0 -135
@@ -24,6 +24,7 @@
24
24
 
25
25
  * ModusBox
26
26
  - Georgi Georgiev <georgi.georgiev@modusbox.com>
27
+ - Georgi Logodazhki <georgi.logodazhki@modusbox.com>
27
28
  - Rajiv Mothilal <rajiv.mothilal@modusbox.com>
28
29
  - Miguel de Barros <miguel.debarros@modusbox.com>
29
30
  - Deon Botha <deon.botha@modusbox.com>
@@ -52,7 +53,7 @@ const TransferEventAction = Enum.Events.Event.Action
52
53
  const TransferObjectTransform = require('../../domain/transfer/transform')
53
54
  const Metrics = require('@mojaloop/central-services-metrics')
54
55
  const Config = require('../../lib/config')
55
- const decodePayload = require('@mojaloop/central-services-shared').Util.StreamingProtocol.decodePayload
56
+ const decodePayload = Util.StreamingProtocol.decodePayload
56
57
  const Comparators = require('@mojaloop/central-services-shared').Util.Comparators
57
58
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
58
59
 
@@ -70,7 +71,7 @@ const toDestination = true
70
71
  * the relevant tables. If the validation fails it is still written to the database for auditing purposes but with an
71
72
  * INVALID status. For any duplicate requests we will send appropriate callback based on the transfer state and the hash validation
72
73
  *
73
- * Validator.validateByName called to validate the payload of the message
74
+ * Validator.validatePrepare called to validate the payload of the message
74
75
  * TransferService.getById called to get the details of the existing transfer
75
76
  * TransferObjectTransform.toTransfer called to transform the transfer object
76
77
  * TransferService.prepare called and creates new entries in transfer tables for successful prepare transfer
@@ -109,69 +110,80 @@ const prepare = async (error, messages) => {
109
110
  span.setTags({ transactionId: transferId })
110
111
  await span.audit(message, EventSdk.AuditEventAction.start)
111
112
  const kafkaTopic = message.topic
112
- Logger.info(Util.breadcrumb(location, { method: 'prepare' }))
113
-
114
- const actionLetter = action === TransferEventAction.PREPARE ? Enum.Events.ActionLetter.prepare
115
- : (action === TransferEventAction.BULK_PREPARE ? Enum.Events.ActionLetter.bulkPrepare
116
- : Enum.Events.ActionLetter.unknown)
117
- let functionality = action === TransferEventAction.PREPARE ? TransferEventType.NOTIFICATION
118
- : (action === TransferEventAction.BULK_PREPARE ? TransferEventType.BULK_PROCESSING
119
- : Enum.Events.ActionLetter.unknown)
113
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { method: 'prepare' }))
114
+
115
+ const actionLetter = action === TransferEventAction.PREPARE
116
+ ? Enum.Events.ActionLetter.prepare
117
+ : (action === TransferEventAction.BULK_PREPARE
118
+ ? Enum.Events.ActionLetter.bulkPrepare
119
+ : Enum.Events.ActionLetter.unknown)
120
+
121
+ let functionality = action === TransferEventAction.PREPARE
122
+ ? TransferEventType.NOTIFICATION
123
+ : (action === TransferEventAction.BULK_PREPARE
124
+ ? TransferEventType.BULK_PROCESSING
125
+ : Enum.Events.ActionLetter.unknown)
120
126
  const params = { message, kafkaTopic, decodedPayload: payload, span, consumer: Consumer, producer: Producer }
121
127
 
122
- Logger.info(Util.breadcrumb(location, { path: 'dupCheck' }))
128
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'dupCheck' }))
129
+ const histTimerDuplicateCheckEnd = Metrics.getHistogram(
130
+ 'handler_transfers',
131
+ 'prepare_duplicateCheckComparator - Metrics for transfer handler',
132
+ ['success', 'funcName']
133
+ ).startTimer()
123
134
 
124
135
  const { hasDuplicateId, hasDuplicateHash } = await Comparators.duplicateCheckComparator(transferId, payload, TransferService.getTransferDuplicateCheck, TransferService.saveTransferDuplicateCheck)
136
+ histTimerDuplicateCheckEnd({ success: true, funcName: 'prepare_duplicateCheckComparator' })
125
137
  if (hasDuplicateId && hasDuplicateHash) {
126
- Logger.info(Util.breadcrumb(location, 'handleResend'))
138
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'handleResend'))
127
139
  const transfer = await TransferService.getByIdLight(transferId)
128
140
  const transferStateEnum = transfer && transfer.transferStateEnumeration
129
141
  const eventDetail = { functionality, action: TransferEventAction.PREPARE_DUPLICATE }
130
142
  if ([TransferState.COMMITTED, TransferState.ABORTED].includes(transferStateEnum)) {
131
- Logger.info(Util.breadcrumb(location, 'finalized'))
143
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'finalized'))
132
144
  if (action === TransferEventAction.PREPARE) {
133
- Logger.info(Util.breadcrumb(location, `callback--${actionLetter}1`))
145
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callback--${actionLetter}1`))
134
146
  message.value.content.payload = TransferObjectTransform.toFulfil(transfer)
135
147
  message.value.content.uriParams = { id: transferId }
136
148
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch })
137
149
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
138
150
  return true
139
151
  } else if (action === TransferEventAction.BULK_PREPARE) {
140
- Logger.info(Util.breadcrumb(location, `validationError1--${actionLetter}2`))
152
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `validationError1--${actionLetter}2`))
141
153
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.MODIFIED_REQUEST, 'Individual transfer prepare duplicate')
142
154
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
143
155
  throw fspiopError
144
156
  }
145
157
  } else {
146
- Logger.info(Util.breadcrumb(location, 'inProgress'))
158
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'inProgress'))
147
159
  if (action === TransferEventAction.BULK_PREPARE) {
148
- Logger.info(Util.breadcrumb(location, `validationError2--${actionLetter}4`))
160
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `validationError2--${actionLetter}4`))
149
161
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.MODIFIED_REQUEST, 'Individual transfer prepare duplicate')
150
162
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
151
163
  throw fspiopError
152
164
  } else { // action === TransferEventAction.PREPARE
153
- Logger.info(Util.breadcrumb(location, `ignore--${actionLetter}3`))
165
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `ignore--${actionLetter}3`))
154
166
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit })
155
167
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
156
168
  return true
157
169
  }
158
170
  }
159
171
  } else if (hasDuplicateId && !hasDuplicateHash) {
160
- Logger.error(Util.breadcrumb(location, `callbackErrorModified1--${actionLetter}5`))
172
+ Logger.isErrorEnabled && Logger.error(Util.breadcrumb(location, `callbackErrorModified1--${actionLetter}5`))
161
173
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.MODIFIED_REQUEST)
162
174
  const eventDetail = { functionality, action }
163
175
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
164
176
  throw fspiopError
165
177
  } else { // !hasDuplicateId
166
- const { validationPassed, reasons } = await Validator.validateByName(payload, headers)
178
+ const { validationPassed, reasons } = await Validator.validatePrepare(payload, headers)
167
179
  if (validationPassed) {
168
- Logger.info(Util.breadcrumb(location, { path: 'validationPassed' }))
180
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'validationPassed' }))
169
181
  try {
170
- Logger.info(Util.breadcrumb(location, 'saveTransfer'))
182
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'saveTransfer'))
171
183
  await TransferService.prepare(payload)
172
184
  } catch (err) {
173
- Logger.info(Util.breadcrumb(location, `callbackErrorInternal1--${actionLetter}6`))
174
- Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
185
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInternal1--${actionLetter}6`))
186
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
175
187
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR)
176
188
  const eventDetail = { functionality, action: TransferEventAction.PREPARE }
177
189
  /**
@@ -179,23 +191,24 @@ const prepare = async (error, messages) => {
179
191
  * HOWTO: Stop execution at the `TransferService.prepare`, stop mysql,
180
192
  * continue execution to catch block, start mysql
181
193
  */
194
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
182
195
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
183
196
  throw fspiopError
184
197
  }
185
- Logger.info(Util.breadcrumb(location, `positionTopic1--${actionLetter}7`))
198
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `positionTopic1--${actionLetter}7`))
186
199
  functionality = TransferEventType.POSITION
187
200
  const eventDetail = { functionality, action }
188
201
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination })
189
202
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
190
203
  return true
191
204
  } else {
192
- Logger.error(Util.breadcrumb(location, { path: 'validationFailed' }))
205
+ Logger.isErrorEnabled && Logger.error(Util.breadcrumb(location, { path: 'validationFailed' }))
193
206
  try {
194
- Logger.info(Util.breadcrumb(location, 'saveInvalidRequest'))
207
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'saveInvalidRequest'))
195
208
  await TransferService.prepare(payload, reasons.toString(), false)
196
209
  } catch (err) {
197
- Logger.info(Util.breadcrumb(location, `callbackErrorInternal2--${actionLetter}8`))
198
- Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
210
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInternal2--${actionLetter}8`))
211
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
199
212
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err, ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR)
200
213
  const eventDetail = { functionality, action: TransferEventAction.PREPARE }
201
214
  /**
@@ -208,7 +221,7 @@ const prepare = async (error, messages) => {
208
221
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
209
222
  throw fspiopError
210
223
  }
211
- Logger.info(Util.breadcrumb(location, `callbackErrorGeneric--${actionLetter}9`))
224
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorGeneric--${actionLetter}9`))
212
225
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, reasons.toString())
213
226
  await TransferService.logTransferError(transferId, ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR.code, reasons.toString())
214
227
  const eventDetail = { functionality, action }
@@ -225,7 +238,7 @@ const prepare = async (error, messages) => {
225
238
  } catch (err) {
226
239
  histTimerEnd({ success: false, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
227
240
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
228
- Logger.error(`${Util.breadcrumb(location)}::${err.message}--P0`)
241
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--P0`)
229
242
  const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message)
230
243
  await span.error(fspiopError, state)
231
244
  await span.finish(fspiopError.message, state)
@@ -263,28 +276,56 @@ const fulfil = async (error, messages) => {
263
276
  const action = message.value.metadata.event.action
264
277
  const transferId = message.value.content.uriParams.id
265
278
  const kafkaTopic = message.topic
266
- Logger.info(Util.breadcrumb(location, { method: `fulfil:${action}` }))
267
-
268
- const actionLetter = action === TransferEventAction.COMMIT ? Enum.Events.ActionLetter.commit
269
- : (action === TransferEventAction.REJECT ? Enum.Events.ActionLetter.reject
270
- : (action === TransferEventAction.ABORT ? Enum.Events.ActionLetter.abort
271
- : (action === TransferEventAction.BULK_COMMIT ? Enum.Events.ActionLetter.bulkCommit
272
- : Enum.Events.ActionLetter.unknown)))
273
- const functionality = action === TransferEventAction.COMMIT ? TransferEventType.NOTIFICATION
274
- : (action === TransferEventAction.REJECT ? TransferEventType.NOTIFICATION
275
- : (action === TransferEventAction.ABORT ? TransferEventType.NOTIFICATION
276
- : (action === TransferEventAction.BULK_COMMIT ? TransferEventType.BULK_PROCESSING
277
- : Enum.Events.ActionLetter.unknown)))
279
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { method: `fulfil:${action}` }))
280
+
281
+ const actionLetter = (() => {
282
+ switch (action) {
283
+ case TransferEventAction.COMMIT: return Enum.Events.ActionLetter.commit
284
+ case TransferEventAction.RESERVE: return Enum.Events.ActionLetter.reserve
285
+ case TransferEventAction.REJECT: return Enum.Events.ActionLetter.reject
286
+ case TransferEventAction.ABORT: return Enum.Events.ActionLetter.abort
287
+ case TransferEventAction.BULK_COMMIT: return Enum.Events.ActionLetter.bulkCommit
288
+ case TransferEventAction.BULK_ABORT: return Enum.Events.ActionLetter.bulkAbort
289
+ default: return Enum.Events.ActionLetter.unknown
290
+ }
291
+ })()
292
+
293
+ const functionality = (() => {
294
+ switch (action) {
295
+ case TransferEventAction.COMMIT:
296
+ case TransferEventAction.RESERVE:
297
+ case TransferEventAction.REJECT:
298
+ case TransferEventAction.ABORT:
299
+ return TransferEventType.NOTIFICATION
300
+ case TransferEventAction.BULK_COMMIT:
301
+ case TransferEventAction.BULK_ABORT:
302
+ return TransferEventType.BULK_PROCESSING
303
+ default: return Enum.Events.ActionLetter.unknown
304
+ }
305
+ })()
306
+
278
307
  // fulfil-specific declarations
279
308
  const isTransferError = action === TransferEventAction.ABORT
280
309
  const params = { message, kafkaTopic, decodedPayload: payload, span, consumer: Consumer, producer: Producer }
281
310
 
282
- Logger.info(Util.breadcrumb(location, { path: 'getById' }))
311
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'getById' }))
312
+
313
+ // We fail early and silently to allow timeout handler abort transfer
314
+ // if 'RESERVED' transfer state is sent in with v1.0 content-type
315
+ if (headers['content-type'].split('=')[1] === '1.0' && payload.transferState === TransferState.RESERVED) {
316
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `failSilentlyforReservedStateWith1.0ContentType--${actionLetter}0`))
317
+ const errorMessage = 'action "RESERVE" is not allowed in fulfil handler for v1.0 clients.'
318
+ Logger.isErrorEnabled && Logger.error(errorMessage)
319
+ !!span && span.error(errorMessage)
320
+ histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
321
+ return true
322
+ }
323
+
283
324
  const transfer = await TransferService.getById(transferId)
284
325
  const transferStateEnum = transfer && transfer.transferStateEnumeration
285
326
 
286
327
  if (!transfer) {
287
- Logger.error(Util.breadcrumb(location, `callbackInternalServerErrorNotFound--${actionLetter}1`))
328
+ Logger.isErrorEnabled && Logger.error(Util.breadcrumb(location, `callbackInternalServerErrorNotFound--${actionLetter}1`))
288
329
  const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError('transfer not found')
289
330
  const eventDetail = { functionality, action: TransferEventAction.COMMIT }
290
331
  /**
@@ -294,28 +335,91 @@ const fulfil = async (error, messages) => {
294
335
  */
295
336
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
296
337
  throw fspiopError
297
- } else if (headers[Enum.Http.Headers.FSPIOP.SOURCE].toLowerCase() !== transfer.payeeFsp.toLowerCase()) {
338
+
339
+ // Lets validate FSPIOP Source & Destination Headers
340
+ } else if ((headers[Enum.Http.Headers.FSPIOP.SOURCE] && (headers[Enum.Http.Headers.FSPIOP.SOURCE].toLowerCase() !== transfer.payeeFsp.toLowerCase())) || (headers[Enum.Http.Headers.FSPIOP.DESTINATION] && (headers[Enum.Http.Headers.FSPIOP.DESTINATION].toLowerCase() !== transfer.payerFsp.toLowerCase()))) {
298
341
  /**
299
- * If fulfilment request is coming from a source not matching transfer payee fsp,
300
- * don't proceed the request, but rather send error callback to original payee fsp.
301
- * This is also the reason why we need to retrieve the transfer info upfront now.
342
+ * If fulfilment request is coming from a source not matching transfer payee fsp or destination not matching transfer payer fsp,
302
343
  */
303
- Logger.info(Util.breadcrumb(location, `callbackErrorSourceNotMatchingPayeeFsp--${actionLetter}2`))
304
- const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, `${Enum.Http.Headers.FSPIOP.SOURCE} does not match payee fsp`)
305
- const toDestination = transfer.payeeFsp // overrding global boolean declaration with a string value for local use only
306
- const eventDetail = { functionality, action: TransferEventAction.COMMIT }
344
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorSourceNotMatchingTransferFSPs--${actionLetter}2`))
345
+
346
+ // Lets set a default non-matching error to fallback-on
347
+ let fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'FSP does not match one of the fsp-id\'s associated with a transfer on the Fulfil callback response')
348
+
349
+ // Lets make the error specific if the PayeeFSP IDs do not match
350
+ if (headers[Enum.Http.Headers.FSPIOP.SOURCE].toLowerCase() !== transfer.payeeFsp.toLowerCase()) {
351
+ fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, `${Enum.Http.Headers.FSPIOP.SOURCE} does not match payee fsp on the Fulfil callback response`)
352
+ }
353
+
354
+ // Lets make the error specific if the PayerFSP IDs do not match
355
+ if (headers[Enum.Http.Headers.FSPIOP.DESTINATION].toLowerCase() !== transfer.payerFsp.toLowerCase()) {
356
+ fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, `${Enum.Http.Headers.FSPIOP.DESTINATION} does not match payer fsp on the Fulfil callback response`)
357
+ }
358
+
359
+ const apiFSPIOPError = fspiopError.toApiErrorObject(Config.ERROR_HANDLING)
360
+
361
+ // Overriding global boolean declaration with a string value for local as we should handle notifications only to FSPs involved with this transfer
362
+ const toPayerDestination = transfer.payerFsp
363
+ const toPayeeDestination = transfer.payeeFsp
364
+
365
+ // Set the event details to map to an ABORT_VALIDATION event targeted to the Position Handler
366
+ const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.ABORT_VALIDATION }
367
+
368
+ // Lets handle the abort validation and change the transfer state to reflect this
369
+ const transferAbortResult = await TransferService.handlePayeeResponse(transferId, payload, TransferEventAction.ABORT_VALIDATION, apiFSPIOPError)
370
+
307
371
  /**
308
372
  * TODO: BULK-Handle at BulkProcessingHandler (not in scope of #967)
309
373
  * HOWTO: For regular transfers, send the fulfil from non-payee dfsp.
310
374
  * Not sure if it will apply to bulk, as it could/should be captured
311
375
  * at BulkPrepareHander. To be verified as part of future story.
312
376
  */
313
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch, toDestination })
314
- throw fspiopError
377
+
378
+ // Publish message to Position Handler
379
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: apiFSPIOPError, eventDetail, fromSwitch, toDestination: toPayerDestination })
380
+
381
+ /**
382
+ * Send patch notification callback to original payee fsp if they asked for a a patch response.
383
+ */
384
+ if (action === TransferEventAction.RESERVE) {
385
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackReservedAborted--${actionLetter}3`))
386
+
387
+ // Set the event details to map to an RESERVE_ABORTED event targeted to the Notification Handler
388
+ const reserveAbortedEventDetail = { functionality: TransferEventType.NOTIFICATION, action: TransferEventAction.RESERVED_ABORTED }
389
+
390
+ // Extract error information
391
+ const errorCode = apiFSPIOPError && apiFSPIOPError.errorInformation && apiFSPIOPError.errorInformation.errorCode
392
+ const errorDescription = apiFSPIOPError && apiFSPIOPError.errorInformation && apiFSPIOPError.errorInformation.errorDescription
393
+
394
+ // TODO: This should be handled by a PATCH /transfers/{id}/error callback in the future FSPIOP v1.2 specification, and instead we should just send the FSPIOP-Error instead! Ref: https://github.com/mojaloop/mojaloop-specification/issues/106.
395
+ const reservedAbortedPayload = {
396
+ transferId: transferAbortResult && transferAbortResult.id,
397
+ completedTimestamp: transferAbortResult && transferAbortResult.completedTimestamp && (new Date(Date.parse(transferAbortResult.completedTimestamp))).toISOString(),
398
+ transferState: TransferState.ABORTED,
399
+ extensionList: { // lets add the extension list to handle the limitation of the FSPIOP v1.1 specification by adding the error cause...
400
+ extension: [
401
+ {
402
+ key: 'cause',
403
+ value: `${errorCode}: ${errorDescription}`
404
+ }
405
+ ]
406
+ }
407
+ }
408
+ message.value.content.payload = reservedAbortedPayload
409
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail: reserveAbortedEventDetail, toDestination: toPayeeDestination, fromSwitch: true })
410
+ }
411
+
412
+ throw apiFSPIOPError
315
413
  }
316
414
  // If execution continues after this point we are sure transfer exists and source matches payee fsp
317
415
 
318
- Logger.info(Util.breadcrumb(location, { path: 'dupCheck' }))
416
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'dupCheck' }))
417
+ const histTimerDuplicateCheckEnd = Metrics.getHistogram(
418
+ 'handler_transfers',
419
+ 'fulfil_duplicateCheckComparator - Metrics for transfer handler',
420
+ ['success', 'funcName']
421
+ ).startTimer()
422
+
319
423
  let dupCheckResult
320
424
  if (!isTransferError) {
321
425
  dupCheckResult = await Comparators.duplicateCheckComparator(transferId, payload, TransferService.getTransferFulfilmentDuplicateCheck, TransferService.saveTransferFulfilmentDuplicateCheck)
@@ -323,29 +427,34 @@ const fulfil = async (error, messages) => {
323
427
  dupCheckResult = await Comparators.duplicateCheckComparator(transferId, payload, TransferService.getTransferErrorDuplicateCheck, TransferService.saveTransferErrorDuplicateCheck)
324
428
  }
325
429
  const { hasDuplicateId, hasDuplicateHash } = dupCheckResult
326
-
430
+ histTimerDuplicateCheckEnd({ success: true, funcName: 'fulfil_duplicateCheckComparator' })
327
431
  if (hasDuplicateId && hasDuplicateHash) {
328
- Logger.info(Util.breadcrumb(location, 'handleResend'))
432
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'handleResend'))
433
+
434
+ // This is a duplicate message for a transfer that is already in a finalized state
435
+ // respond as if we recieved a GET /transfers/{ID} from the client
329
436
  if (transferStateEnum === TransferState.COMMITTED || transferStateEnum === TransferState.ABORTED) {
330
437
  message.value.content.payload = TransferObjectTransform.toFulfil(transfer)
331
- if (!isTransferError) {
332
- Logger.info(Util.breadcrumb(location, `callbackFinilized2--${actionLetter}3`))
333
- const eventDetail = { functionality, action: TransferEventAction.FULFIL_DUPLICATE }
334
- /**
335
- * HOWTO: During bulk fulfil use an individualTransfer from a previous bulk fulfil
336
- */
337
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch })
338
- histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
339
- return true
340
- } else {
341
- Logger.info(Util.breadcrumb(location, `callbackFinilized3--${actionLetter}4`))
342
- const eventDetail = { functionality, action: TransferEventAction.ABORT_DUPLICATE }
343
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch })
344
- histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
345
- return true
438
+ const eventDetail = { functionality, action }
439
+ if (action !== TransferEventAction.RESERVE) {
440
+ if (!isTransferError) {
441
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackFinalized2--${actionLetter}3`))
442
+ eventDetail.action = TransferEventAction.FULFIL_DUPLICATE
443
+ /**
444
+ * HOWTO: During bulk fulfil use an individualTransfer from a previous bulk fulfil
445
+ */
446
+ } else {
447
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackFinalized3--${actionLetter}4`))
448
+ eventDetail.action = TransferEventAction.ABORT_DUPLICATE
449
+ }
346
450
  }
347
- } else if (transferStateEnum === TransferState.RECEIVED || transferStateEnum === TransferState.RESERVED) {
348
- Logger.info(Util.breadcrumb(location, `inProgress2--${actionLetter}5`))
451
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch })
452
+ histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
453
+ return true
454
+ }
455
+
456
+ if (transferStateEnum === TransferState.RECEIVED || transferStateEnum === TransferState.RESERVED) {
457
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `inProgress2--${actionLetter}5`))
349
458
  /**
350
459
  * HOWTO: Nearly impossible to trigger for bulk - an individual transfer from a bulk needs to be triggered
351
460
  * for processing in order to have the fulfil duplicate hash recorded. While it is still in RESERVED state
@@ -356,122 +465,228 @@ const fulfil = async (error, messages) => {
356
465
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, histTimerEnd })
357
466
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
358
467
  return true
359
- } else {
360
- Logger.info(Util.breadcrumb(location, `callbackErrorInvalidTransferStateEnum--${actionLetter}6`))
361
- const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(
362
- `Invalid transferStateEnumeration:(${transferStateEnum}) for event action:(${action}) and type:(${type})`).toApiErrorObject(Config.ERROR_HANDLING)
363
- const eventDetail = { functionality, action: TransferEventAction.COMMIT }
364
- /**
365
- * HOWTO: Impossible to trigger for individual transfer in a bulk? (not in scope of #967)
366
- */
367
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError, eventDetail, fromSwitch })
368
- histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
369
- return true
370
468
  }
371
- } else if (hasDuplicateId && !hasDuplicateHash) {
372
- let eventDetail
469
+
470
+ // Error scenario - transfer.transferStateEnumeration is in some invalid state
471
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInvalidTransferStateEnum--${actionLetter}6`))
472
+ const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(
473
+ `Invalid transferStateEnumeration:(${transferStateEnum}) for event action:(${action}) and type:(${type})`).toApiErrorObject(Config.ERROR_HANDLING)
474
+ const eventDetail = { functionality, action: TransferEventAction.COMMIT }
475
+ /**
476
+ * HOWTO: Impossible to trigger for individual transfer in a bulk? (not in scope of #967)
477
+ */
478
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError, eventDetail, fromSwitch })
479
+ histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
480
+ return true
481
+ }
482
+
483
+ // ERROR: We have seen a transfer of this ID before, but it's message hash doesn't match
484
+ // the previous message hash.
485
+ if (hasDuplicateId && !hasDuplicateHash) {
373
486
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.MODIFIED_REQUEST)
374
- if (!isTransferError) {
375
- Logger.info(Util.breadcrumb(location, `callbackErrorModified2--${actionLetter}7`))
376
- eventDetail = { functionality, action: TransferEventAction.FULFIL_DUPLICATE }
377
- /**
378
- * HOWTO: During bulk fulfil use an individualTransfer from a previous bulk fulfil,
379
- * but use different fulfilment value.
380
- */
381
- } else {
382
- Logger.info(Util.breadcrumb(location, `callbackErrorModified3--${actionLetter}8`))
383
- eventDetail = { functionality, action: TransferEventAction.ABORT_DUPLICATE }
487
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorModified2--${actionLetter}7`))
488
+ let action = TransferEventAction.FULFIL_DUPLICATE
489
+ if (isTransferError) {
490
+ action = TransferEventAction.ABORT_DUPLICATE
384
491
  }
492
+
493
+ /**
494
+ * HOWTO: During bulk fulfil use an individualTransfer from a previous bulk fulfil,
495
+ * but use different fulfilment value.
496
+ */
497
+ const eventDetail = { functionality, action }
385
498
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
386
499
  throw fspiopError
387
- } else { // !hasDuplicateId
388
- if (type === TransferEventType.FULFIL && [TransferEventAction.COMMIT, TransferEventAction.REJECT, TransferEventAction.ABORT, TransferEventAction.BULK_COMMIT].includes(action)) {
389
- Util.breadcrumb(location, { path: 'validationFailed' })
390
- if (payload.fulfilment && !Validator.validateFulfilCondition(payload.fulfilment, transfer.condition)) {
391
- Logger.info(Util.breadcrumb(location, `callbackErrorInvalidFulfilment--${actionLetter}9`))
392
- const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'invalid fulfilment')
393
- const apiFspiopError = fspiopError.toApiErrorObject(Config.ERROR_HANDLING)
394
- await TransferService.handlePayeeResponse(transferId, payload, action, apiFspiopError)
395
- const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.ABORT }
396
- /**
397
- * TODO: BulkProcessingHandler (not in scope of #967) The individual transfer is ABORTED by notification is never sent.
398
- */
399
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: apiFspiopError, eventDetail, toDestination })
400
- throw fspiopError
401
- } else if (transfer.transferState !== TransferState.RESERVED) {
402
- Logger.info(Util.breadcrumb(location, `callbackErrorNonReservedState--${actionLetter}10`))
403
- const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'non-RESERVED transfer state')
404
- const eventDetail = { functionality, action: TransferEventAction.COMMIT }
405
- /**
406
- * TODO: BulkProcessingHandler (not in scope of #967)
407
- */
408
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
409
- throw fspiopError
410
- } else if (transfer.expirationDate <= new Date(Util.Time.getUTCString(new Date()))) {
411
- Logger.info(Util.breadcrumb(location, `callbackErrorTransferExpired--${actionLetter}11`))
412
- const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED)
413
- const eventDetail = { functionality, action: TransferEventAction.COMMIT }
500
+ }
501
+
502
+ // Transfer is not a duplicate, or message hasn't been changed.
503
+
504
+ if (type !== TransferEventType.FULFIL) {
505
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInvalidEventType--${actionLetter}15`))
506
+ const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(`Invalid event type:(${type})`)
507
+ const eventDetail = { functionality, action: TransferEventAction.COMMIT }
508
+ /**
509
+ * TODO: BulkProcessingHandler (not in scope of #967)
510
+ */
511
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
512
+ throw fspiopError
513
+ }
514
+
515
+ const validActions = [
516
+ TransferEventAction.COMMIT,
517
+ TransferEventAction.RESERVE,
518
+ TransferEventAction.REJECT,
519
+ TransferEventAction.ABORT,
520
+ TransferEventAction.BULK_COMMIT,
521
+ TransferEventAction.BULK_ABORT
522
+ ]
523
+ if (!validActions.includes(action)) {
524
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInvalidEventAction--${actionLetter}15`))
525
+ const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(`Invalid event action:(${action}) and/or type:(${type})`)
526
+ const eventDetail = { functionality, action: TransferEventAction.COMMIT }
527
+ /**
528
+ * TODO: BulkProcessingHandler (not in scope of #967)
529
+ */
530
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
531
+ throw fspiopError
532
+ }
533
+
534
+ Util.breadcrumb(location, { path: 'validationCheck' })
535
+ if (payload.fulfilment && !Validator.validateFulfilCondition(payload.fulfilment, transfer.condition)) {
536
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInvalidFulfilment--${actionLetter}9`))
537
+ const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'invalid fulfilment')
538
+ const apiFSPIOPError = fspiopError.toApiErrorObject(Config.ERROR_HANDLING)
539
+ await TransferService.handlePayeeResponse(transferId, payload, action, apiFSPIOPError)
540
+ const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.ABORT_VALIDATION }
541
+ /**
542
+ * TODO: BulkProcessingHandler (not in scope of #967) The individual transfer is ABORTED by notification is never sent.
543
+ */
544
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: apiFSPIOPError, eventDetail, toDestination })
545
+
546
+ // emit an extra message - RESERVED_ABORTED if action === TransferEventAction.RESERVE
547
+ if (action === TransferEventAction.RESERVE) {
548
+ // Get the updated transfer now that completedTimestamp will be different
549
+ // TODO: should we just modify TransferService.handlePayeeResponse to
550
+ // return the completed timestamp? Or is it safer to go back to the DB here?
551
+ const transferAbortResult = await TransferService.getById(transferId)
552
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackReservedAborted--${actionLetter}1`))
553
+ const eventDetail = { functionality: TransferEventType.NOTIFICATION, action: TransferEventAction.RESERVED_ABORTED }
554
+
555
+ // Extract error information
556
+ const errorCode = apiFSPIOPError && apiFSPIOPError.errorInformation && apiFSPIOPError.errorInformation.errorCode
557
+ const errorDescription = apiFSPIOPError && apiFSPIOPError.errorInformation && apiFSPIOPError.errorInformation.errorDescription
558
+
559
+ // TODO: This should be handled by a PATCH /transfers/{id}/error callback in the future FSPIOP v1.2 specification, and instead we should just send the FSPIOP-Error instead! Ref: https://github.com/mojaloop/mojaloop-specification/issues/106.
560
+ const reservedAbortedPayload = {
561
+ transferId: transferAbortResult && transferAbortResult.id,
562
+ completedTimestamp: transferAbortResult && transferAbortResult.completedTimestamp && (new Date(Date.parse(transferAbortResult.completedTimestamp))).toISOString(),
563
+ transferState: TransferState.ABORTED,
564
+ extensionList: { // lets add the extension list to handle the limitation of the FSPIOP v1.1 specification by adding the error cause...
565
+ extension: [
566
+ {
567
+ key: 'cause',
568
+ value: `${errorCode}: ${errorDescription}`
569
+ }
570
+ ]
571
+ }
572
+ }
573
+ message.value.content.payload = reservedAbortedPayload
574
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination: transfer.payeeFsp, fromSwitch: true })
575
+ }
576
+ throw fspiopError
577
+ }
578
+
579
+ if (transfer.transferState !== TransferState.RESERVED) {
580
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorNonReservedState--${actionLetter}10`))
581
+ const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'non-RESERVED transfer state')
582
+ const eventDetail = { functionality, action: TransferEventAction.COMMIT }
583
+ /**
584
+ * TODO: BulkProcessingHandler (not in scope of #967)
585
+ */
586
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
587
+
588
+ // emit an extra message - RESERVED_ABORTED if action === TransferEventAction.RESERVE
589
+ if (action === TransferEventAction.RESERVE) {
590
+ // Get the updated transfer now that completedTimestamp will be different
591
+ // TODO: should we just modify TransferService.handlePayeeResponse to
592
+ // return the completed timestamp? Or is it safer to go back to the DB here?
593
+ const transferAborted = await TransferService.getById(transferId) // TODO: remove this once it can be tested
594
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackReservedAborted--${actionLetter}2`))
595
+ const eventDetail = { functionality: TransferEventType.NOTIFICATION, action: TransferEventAction.RESERVED_ABORTED }
596
+ const reservedAbortedPayload = {
597
+ transferId: transferAborted.id,
598
+ completedTimestamp: Util.Time.getUTCString(new Date(transferAborted.completedTimestamp)), // TODO: remove this once it can be tested
599
+ transferState: TransferState.ABORTED
600
+ }
601
+ message.value.content.payload = reservedAbortedPayload
602
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination: transfer.payeeFsp, fromSwitch: true })
603
+ }
604
+ throw fspiopError
605
+ }
606
+
607
+ if (transfer.expirationDate <= new Date(Util.Time.getUTCString(new Date()))) {
608
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorTransferExpired--${actionLetter}11`))
609
+ const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED)
610
+ const eventDetail = { functionality, action: TransferEventAction.COMMIT }
611
+ /**
612
+ * TODO: BulkProcessingHandler (not in scope of #967)
613
+ */
614
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
615
+
616
+ // emit an extra message - RESERVED_ABORTED if action === TransferEventAction.RESERVE
617
+ if (action === TransferEventAction.RESERVE) {
618
+ // Get the updated transfer now that completedTimestamp will be different
619
+ // TODO: should we just modify TransferService.handlePayeeResponse to
620
+ // return the completed timestamp? Or is it safer to go back to the DB here?
621
+ const transferAborted = await TransferService.getById(transferId)
622
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackReservedAborted--${actionLetter}3`))
623
+ const eventDetail = { functionality: TransferEventType.NOTIFICATION, action: TransferEventAction.RESERVED_ABORTED }
624
+ const reservedAbortedPayload = {
625
+ transferId: transferAborted.id,
626
+ completedTimestamp: Util.Time.getUTCString(new Date(transferAborted.completedTimestamp)),
627
+ transferState: TransferState.ABORTED
628
+ }
629
+ message.value.content.payload = reservedAbortedPayload
630
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination: transfer.payeeFsp, fromSwitch: true })
631
+ }
632
+ throw fspiopError
633
+ }
634
+
635
+ // Validations Succeeded - process the fulfil
636
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'validationPassed' }))
637
+ switch (action) {
638
+ case TransferEventAction.COMMIT:
639
+ case TransferEventAction.RESERVE:
640
+ case TransferEventAction.BULK_COMMIT: {
641
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `positionTopic2--${actionLetter}12`))
642
+ await TransferService.handlePayeeResponse(transferId, payload, action)
643
+ const eventDetail = { functionality: TransferEventType.POSITION, action }
644
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination })
645
+ histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
646
+ return true
647
+ }
648
+ // TODO: why do we let this logic get this far? Why not remove it from validActions array above?
649
+ case TransferEventAction.REJECT: {
650
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `positionTopic3--${actionLetter}13`))
651
+ const errorMessage = 'action REJECT is not allowed into fulfil handler'
652
+ Logger.isErrorEnabled && Logger.error(errorMessage)
653
+ !!span && span.error(errorMessage)
654
+ histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
655
+ return true
656
+ }
657
+ // TODO: why do we let this logic get this far? Why not remove it from validActions array above?
658
+ case TransferEventAction.ABORT:
659
+ case TransferEventAction.BULK_ABORT:
660
+ default: { // action === TransferEventAction.ABORT || action === TransferEventAction.BULK_ABORT // error-callback request to be processed
661
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `positionTopic4--${actionLetter}14`))
662
+ let fspiopError
663
+ const eInfo = payload.errorInformation
664
+ try { // handle only valid errorCodes provided by the payee
665
+ fspiopError = ErrorHandler.Factory.createFSPIOPErrorFromErrorInformation(eInfo)
666
+ } catch (err) {
414
667
  /**
415
- * TODO: BulkProcessingHandler (not in scope of #967)
668
+ * TODO: Handling of out-of-range errorCodes is to be introduced to the ml-api-adapter,
669
+ * so that such requests are rejected right away, instead of aborting the transfer here.
416
670
  */
417
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
671
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}`)
672
+ fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'API specification undefined errorCode')
673
+ await TransferService.handlePayeeResponse(transferId, payload, action, fspiopError.toApiErrorObject(Config.ERROR_HANDLING))
674
+ const eventDetail = { functionality: TransferEventType.POSITION, action }
675
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, toDestination })
418
676
  throw fspiopError
419
- } else { // validations success
420
- Logger.info(Util.breadcrumb(location, { path: 'validationPassed' }))
421
- if ([TransferEventAction.COMMIT, TransferEventAction.BULK_COMMIT].includes(action)) {
422
- Logger.info(Util.breadcrumb(location, `positionTopic2--${actionLetter}12`))
423
- await TransferService.handlePayeeResponse(transferId, payload, action)
424
- const eventDetail = { functionality: TransferEventType.POSITION, action }
425
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination })
426
- histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
427
- return true
428
- } else {
429
- if (action === TransferEventAction.REJECT) {
430
- Logger.info(Util.breadcrumb(location, `positionTopic3--${actionLetter}13`))
431
- await TransferService.handlePayeeResponse(transferId, payload, action)
432
- const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.REJECT }
433
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, toDestination })
434
- histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
435
- return true
436
- } else { // action === TransferEventAction.ABORT // error-callback request to be processed
437
- Logger.info(Util.breadcrumb(location, `positionTopic4--${actionLetter}14`))
438
- let fspiopError
439
- const eInfo = payload.errorInformation
440
- try { // handle only valid errorCodes provided by the payee
441
- fspiopError = ErrorHandler.Factory.createFSPIOPErrorFromErrorInformation(eInfo)
442
- } catch (err) {
443
- /**
444
- * TODO: Handling of out-of-range errorCodes is to be introduced to the ml-api-adapter,
445
- * so that such requests are rejected right away, instead of aborting the transfer here.
446
- */
447
- fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'API specification undefined errorCode')
448
- await TransferService.handlePayeeResponse(transferId, payload, action, fspiopError.toApiErrorObject(Config.ERROR_HANDLING))
449
- const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.ABORT }
450
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, toDestination })
451
- throw fspiopError
452
- }
453
- await TransferService.handlePayeeResponse(transferId, payload, action, fspiopError.toApiErrorObject(Config.ERROR_HANDLING))
454
- const eventDetail = { functionality: TransferEventType.POSITION, action: TransferEventAction.ABORT }
455
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, toDestination })
456
- throw fspiopError
457
- }
458
- }
459
677
  }
460
- } else {
461
- Logger.info(Util.breadcrumb(location, `callbackErrorInvalidEventAction--${actionLetter}15`))
462
- const fspiopError = ErrorHandler.Factory.createInternalServerFSPIOPError(`Invalid event action:(${action}) and/or type:(${type})`)
463
- const eventDetail = { functionality, action: TransferEventAction.COMMIT }
464
- /**
465
- * TODO: BulkProcessingHandler (not in scope of #967)
466
- */
467
- await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
678
+ await TransferService.handlePayeeResponse(transferId, payload, action, fspiopError.toApiErrorObject(Config.ERROR_HANDLING))
679
+ const eventDetail = { functionality: TransferEventType.POSITION, action }
680
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, toDestination })
681
+ // TODO(2556): I don't think we should emit an extra notification here
682
+ // this is the case where the Payee sent an ABORT, so we don't need to tell them to abort
468
683
  throw fspiopError
469
684
  }
470
685
  }
471
686
  } catch (err) {
472
687
  histTimerEnd({ success: false, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
473
688
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
474
- Logger.error(`${Util.breadcrumb(location)}::${err.message}--F0`)
689
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--F0`)
475
690
  const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message)
476
691
  await span.error(fspiopError, state)
477
692
  await span.finish(fspiopError.message, state)
@@ -516,7 +731,7 @@ const getTransfer = async (error, messages) => {
516
731
  const action = metadata.event.action
517
732
  const transferId = message.value.content.uriParams.id
518
733
  const kafkaTopic = message.topic
519
- Logger.info(Util.breadcrumb(location, { method: `getTransfer:${action}` }))
734
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { method: `getTransfer:${action}` }))
520
735
 
521
736
  const actionLetter = Enum.Events.ActionLetter.get
522
737
  const params = { message, kafkaTopic, span, consumer: Consumer, producer: Producer }
@@ -524,27 +739,34 @@ const getTransfer = async (error, messages) => {
524
739
 
525
740
  Util.breadcrumb(location, { path: 'validationFailed' })
526
741
  if (!await Validator.validateParticipantByName(message.value.from)) {
527
- Logger.info(Util.breadcrumb(location, `breakParticipantDoesntExist--${actionLetter}1`))
742
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `breakParticipantDoesntExist--${actionLetter}1`))
528
743
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, histTimerEnd })
529
744
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
530
745
  return true
531
746
  }
532
747
  const transfer = await TransferService.getByIdLight(transferId)
533
748
  if (!transfer) {
534
- Logger.info(Util.breadcrumb(location, `callbackErrorTransferNotFound--${actionLetter}3`))
749
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorTransferNotFound--${actionLetter}3`))
535
750
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_ID_NOT_FOUND, 'Provided Transfer ID was not found on the server.')
536
751
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
537
752
  throw fspiopError
538
753
  }
539
754
  if (!await Validator.validateParticipantTransferId(message.value.from, transferId)) {
540
- Logger.info(Util.breadcrumb(location, `callbackErrorNotTransferParticipant--${actionLetter}2`))
755
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorNotTransferParticipant--${actionLetter}2`))
541
756
  const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.CLIENT_ERROR)
542
757
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
543
758
  throw fspiopError
544
759
  }
760
+ if (transfer.transferState === Enum.Transfers.TransferInternalState.EXPIRED_RESERVED) {
761
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackTransferExpired--${actionLetter}3`))
762
+ const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.TRANSFER_EXPIRED)
763
+ await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: fspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
764
+ throw fspiopError
765
+ }
766
+
545
767
  // ============================================================================================
546
768
  Util.breadcrumb(location, { path: 'validationPassed' })
547
- Logger.info(Util.breadcrumb(location, `callbackMessage--${actionLetter}4`))
769
+ Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackMessage--${actionLetter}4`))
548
770
  message.value.content.payload = TransferObjectTransform.toFulfil(transfer)
549
771
  await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, eventDetail, fromSwitch })
550
772
  histTimerEnd({ success: true, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
@@ -552,7 +774,7 @@ const getTransfer = async (error, messages) => {
552
774
  } catch (err) {
553
775
  histTimerEnd({ success: false, fspId: Config.INSTRUMENTATION_METRICS_LABELS.fspId })
554
776
  const fspiopError = ErrorHandler.Factory.reformatFSPIOPError(err)
555
- Logger.error(`${Util.breadcrumb(location)}::${err.message}--G0`)
777
+ Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--G0`)
556
778
  const state = new EventSdk.EventStateMetadata(EventSdk.EventStatusType.failed, fspiopError.apiErrorCode.code, fspiopError.apiErrorCode.message)
557
779
  await span.error(fspiopError, state)
558
780
  await span.finish(fspiopError.message, state)
@@ -583,6 +805,7 @@ const registerPrepareHandler = async () => {
583
805
  await Consumer.createHandler(prepareHandler.topicName, prepareHandler.config, prepareHandler.command)
584
806
  return true
585
807
  } catch (err) {
808
+ Logger.isErrorEnabled && Logger.error(err)
586
809
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
587
810
  }
588
811
  }
@@ -606,6 +829,7 @@ const registerFulfilHandler = async () => {
606
829
  await Consumer.createHandler(fulfillHandler.topicName, fulfillHandler.config, fulfillHandler.command)
607
830
  return true
608
831
  } catch (err) {
832
+ Logger.isErrorEnabled && Logger.error(err)
609
833
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
610
834
  }
611
835
  }
@@ -629,6 +853,7 @@ const registerGetTransferHandler = async () => {
629
853
  await Consumer.createHandler(getHandler.topicName, getHandler.config, getHandler.command)
630
854
  return true
631
855
  } catch (err) {
856
+ Logger.isErrorEnabled && Logger.error(err)
632
857
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
633
858
  }
634
859
  }
@@ -648,6 +873,7 @@ const registerAllHandlers = async () => {
648
873
  await registerGetTransferHandler()
649
874
  return true
650
875
  } catch (err) {
876
+ Logger.isErrorEnabled && Logger.error(err)
651
877
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
652
878
  }
653
879
  }