@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.
- package/.circleci/config.yml +625 -204
- package/.env +3 -2
- package/.ncurc.yaml +9 -0
- package/.nvmrc +1 -1
- package/.versionrc +15 -0
- package/CHANGELOG.md +353 -0
- package/CODEOWNERS +31 -0
- package/Dockerfile +20 -14
- package/LICENSE.md +4 -4
- package/Onboarding.md +0 -7
- package/README.md +99 -22
- package/audit-resolve.json +136 -1
- package/config/default.json +82 -27
- package/docker/central-ledger/default.json +68 -21
- package/docker/ml-api-adapter/default.json +10 -1
- package/docker/wait-for/wait-for-objstore.sh +1 -6
- package/docker/wait-for/wait-for.env +1 -1
- package/docker-compose.integration.yml +8 -5
- package/docker-compose.yml +73 -16
- package/migrations/310203_transferParticipant-indexes.js +38 -0
- package/migrations/310503_participantLimit-indexes-composite.js +38 -0
- package/migrations/{400401_settlementWindowStateChange-indexes.js → 400401_settlmentWindowStateChange-indexes.js} +0 -0
- package/migrations/401301_settlementModel-indexes.js +2 -0
- package/migrations/500401_quote-hotfix-2719.js +58 -0
- package/migrations/500501_feature-fixSubIdRef.js +53 -0
- package/migrations/500601_party-2480.js +63 -0
- package/migrations/501001_quoteExtension-quote-service-174.js +46 -0
- package/migrations/501002_quoteExtension-2522.js +61 -0
- package/migrations/800101_feature-fixSubIdRef.js +90 -0
- package/migrations/910102_hotfix954.js +4 -1
- package/migrations/940100_quotePartyIdInfoExtension.js +52 -0
- package/migrations/940101_quotePartyIdInfoExtension-2522.js +64 -0
- package/migrations/950100_settlementContentAggregation-decimal184.js +40 -0
- package/migrations/950101_transferParticipantStateChange.js +46 -0
- package/migrations/950102_settlementModel-adjustPosition.js +42 -0
- package/migrations/950103_dropTransferParticipantStateChange.js +47 -0
- package/migrations/950104_settlementModel-settlementAccountTypeId.js +79 -0
- package/migrations/950106_unique_settlement_model_ledger_account_currency.js +38 -0
- package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +60 -0
- package/package.json +99 -81
- package/scripts/_wait4_all.js +146 -0
- package/seeds/amountType.js +4 -6
- package/seeds/balanceOfPayments.js +4 -6
- package/seeds/bulkProcessingState.js +4 -6
- package/seeds/bulkTransferState.js +4 -6
- package/seeds/currency.js +4 -6
- package/seeds/endpointType.js +128 -7
- package/seeds/ledgerAccountType.js +11 -10
- package/seeds/ledgerEntryType.js +5 -7
- package/seeds/participant.js +4 -6
- package/seeds/participantLimitType.js +4 -6
- package/seeds/partyIdentifierType.js +4 -6
- package/seeds/partyType.js +4 -6
- package/seeds/settlementDelay.js +4 -6
- package/seeds/settlementGranularity.js +4 -6
- package/seeds/settlementInterchange.js +4 -6
- package/seeds/settlementState.js +4 -6
- package/seeds/settlementWindow1State.js +4 -6
- package/seeds/transactionInitiator.js +4 -6
- package/seeds/transactionInitiatorType.js +4 -6
- package/seeds/transactionScenario.js +4 -6
- package/seeds/transferParticipantRoleType.js +4 -6
- package/seeds/transferState.js +9 -6
- package/src/api/interface/swagger.json +728 -948
- package/src/api/ledgerAccountTypes/handler.js +55 -0
- package/src/api/ledgerAccountTypes/routes.js +63 -0
- package/src/api/metrics/handler.js +2 -2
- package/src/api/metrics/routes.js +1 -1
- package/src/api/participants/handler.js +43 -35
- package/src/api/participants/routes.js +13 -13
- package/src/api/root/routes.js +2 -2
- package/src/api/settlementModels/handler.js +9 -26
- package/src/api/settlementModels/routes.js +4 -4
- package/src/api/transactions/handler.js +46 -0
- package/src/api/transactions/routes.js +45 -0
- package/src/domain/bulkTransfer/index.js +6 -5
- package/src/domain/ledgerAccountTypes/index.js +58 -0
- package/src/domain/participant/index.js +55 -4
- package/src/domain/position/index.js +18 -2
- package/src/domain/settlement/index.js +39 -6
- package/src/domain/transactions/index.js +61 -0
- package/src/domain/transfer/index.js +22 -2
- package/src/domain/transfer/transform.js +3 -3
- package/src/handlers/admin/handler.js +24 -32
- package/src/handlers/bulk/fulfil/handler.js +89 -56
- package/src/handlers/bulk/get/handler.js +203 -0
- package/src/handlers/bulk/index.js +4 -1
- package/src/handlers/bulk/prepare/handler.js +30 -29
- package/src/handlers/bulk/processing/handler.js +61 -32
- package/src/handlers/bulk/shared/validator.js +13 -1
- package/src/handlers/index.js +18 -9
- package/src/handlers/positions/handler.js +67 -46
- package/src/handlers/register.js +7 -7
- package/src/handlers/timeouts/handler.js +9 -4
- package/src/handlers/transfers/handler.js +408 -182
- package/src/handlers/transfers/validator.js +41 -11
- package/src/lib/cache.js +8 -34
- package/src/lib/config.js +5 -7
- package/src/lib/enum.js +22 -22
- package/src/lib/enumCached.js +76 -0
- package/src/lib/healthCheck/subServiceHealth.js +2 -2
- package/src/lib/requestLogger.js +9 -9
- package/src/lib/urlParser.js +1 -1
- package/src/models/bulkTransfer/bulkTransfer.js +6 -6
- package/src/models/bulkTransfer/bulkTransferAssociation.js +3 -3
- package/src/models/bulkTransfer/bulkTransferDuplicateCheck.js +4 -4
- package/src/models/bulkTransfer/bulkTransferExtension.js +2 -2
- package/src/models/bulkTransfer/bulkTransferFulfilmentDuplicateCheck.js +4 -4
- package/src/models/bulkTransfer/bulkTransferStateChange.js +7 -7
- package/src/models/bulkTransfer/facade.js +54 -3
- package/src/models/bulkTransfer/individualTransfer.js +2 -2
- package/src/models/ilpPackets/ilpPacket.js +36 -0
- package/src/models/ledgerAccountType/ledgerAccountType.js +139 -3
- package/src/models/misc/migrationLock.js +1 -1
- package/src/models/misc/segment.js +1 -1
- package/src/models/participant/facade.js +226 -76
- package/src/models/participant/participant.js +6 -5
- package/src/models/participant/participantCached.js +8 -0
- package/src/models/participant/participantCurrency.js +7 -7
- package/src/models/participant/participantCurrencyCached.js +11 -1
- package/src/models/participant/participantLimit.js +13 -13
- package/src/models/participant/participantLimitCached.js +124 -0
- package/src/models/participant/participantPosition.js +34 -5
- package/src/models/participant/participantPositionChange.js +10 -2
- package/src/models/position/facade.js +112 -18
- package/src/models/position/participantPosition.js +6 -6
- package/src/models/settlement/settlementModel.js +92 -17
- package/src/models/settlement/settlementModelCached.js +139 -0
- package/src/models/transfer/facade.js +97 -32
- package/src/models/transfer/ilpPacket.js +4 -4
- package/src/models/transfer/transfer.js +7 -7
- package/src/models/transfer/transferDuplicateCheck.js +21 -5
- package/src/models/transfer/transferError.js +5 -5
- package/src/models/transfer/transferErrorDuplicateCheck.js +4 -4
- package/src/models/transfer/transferExtension.js +4 -4
- package/src/models/transfer/transferFulfilment.js +5 -5
- package/src/models/transfer/transferFulfilmentDuplicateCheck.js +4 -4
- package/src/models/transfer/transferParticipant.js +3 -3
- package/src/models/transfer/transferStateChange.js +11 -11
- package/src/models/transfer/transferTimeout.js +5 -5
- package/src/schema/bulkTransfer.js +4 -4
- package/src/shared/plugins.js +10 -9
- package/src/shared/setup.js +69 -46
- package/test/integration/domain/participant/index.test.js +2 -0
- package/test/integration/handlers/handlers.test.js +605 -166
- package/test/integration/handlers/root.test.js +1 -1
- package/test/integration/helpers/hubAccounts.js +62 -0
- package/test/integration/helpers/ilpPacket.js +1 -1
- package/test/integration/helpers/participantFundsInOut.js +80 -0
- package/test/integration/helpers/participantLimit.js +6 -1
- package/test/integration/helpers/settlementModels.js +88 -0
- package/test/integration/helpers/testConsumer.js +163 -0
- package/test/integration/helpers/testProducer.js +0 -1
- package/test/integration/helpers/transferDuplicateCheck.js +1 -1
- package/test/integration/helpers/transferStateChange.js +1 -1
- package/test/integration/helpers/transferTestHelper.js +3 -5
- package/test/integration/models/transfer/transferStateChange.test.js +6 -0
- package/test/unit/api/ledgerAccountTypes/handler.test.js +126 -0
- package/test/unit/api/ledgerAccountTypes/routes.test.js +52 -0
- package/test/unit/api/participants/handler.test.js +75 -33
- package/test/unit/api/root/handler.test.js +1 -1
- package/test/unit/api/root/routes.test.js +2 -2
- package/test/unit/api/settlementModels/handler.test.js +21 -57
- package/test/unit/api/transactions/handler.test.js +108 -0
- package/test/unit/api/transactions/routes.test.js +38 -0
- package/test/unit/domain/ledgerAccountTypes/index.test.js +197 -0
- package/test/unit/domain/participant/index.test.js +115 -2
- package/test/unit/domain/settlement/index.test.js +199 -29
- package/test/unit/domain/transactions/index.test.js +128 -0
- package/test/unit/domain/transfer/index.test.js +1 -0
- package/test/unit/domain/transfer/transform.test.js +2 -0
- package/test/unit/handlers/admin/handler.test.js +5 -5
- package/test/unit/handlers/bulk/get/handler.test.js +401 -0
- package/test/unit/handlers/index.test.js +6 -0
- package/test/unit/handlers/positions/handler.test.js +26 -0
- package/test/unit/handlers/register.test.js +2 -0
- package/test/unit/handlers/transfers/handler.test.js +491 -36
- package/test/unit/handlers/transfers/validator.test.js +31 -31
- package/test/unit/lib/cache.test.js +0 -64
- package/test/unit/lib/cachingOfEnums.test.js +121 -0
- package/test/unit/lib/config.test.js +34 -0
- package/test/unit/lib/enum.test.js +6 -1
- package/test/unit/lib/enumCached.test.js +82 -0
- package/test/unit/lib/healthCheck/subServiceHealth.test.js +2 -0
- package/test/unit/lib/requestLogger.test.js +14 -12
- package/test/unit/models/ilpPackets/ilpPacket.test.js +89 -0
- package/test/unit/models/ledgerAccountType/ledgerAccountType.test.js +255 -6
- package/test/unit/models/misc/migrationLock.test.js +4 -0
- package/test/unit/models/misc/segment.test.js +5 -0
- package/test/unit/models/participant/facade.test.js +493 -54
- package/test/unit/models/participant/participant.test.js +4 -0
- package/test/unit/models/participant/participantCurrency.test.js +22 -9
- package/test/unit/models/participant/participantCurrencyCached.test.js +1 -1
- package/test/unit/models/participant/participantLimit.test.js +97 -0
- package/test/unit/models/participant/participantLimitCached.test.js +232 -0
- package/test/unit/models/participant/participantPosition.test.js +217 -0
- package/test/unit/models/participant/participantPositionChange.test.js +3 -0
- package/test/unit/models/position/facade.test.js +318 -29
- package/test/unit/models/position/participantPosition.test.js +22 -7
- package/test/unit/models/settlement/settlementModel.test.js +18 -37
- package/test/unit/models/settlement/settlementModelCached.test.js +171 -0
- package/test/unit/models/transfer/facade.test.js +115 -0
- package/test/unit/models/transfer/ilpPacket.test.js +28 -11
- package/test/unit/models/transfer/transfer.test.js +13 -4
- package/test/unit/models/transfer/transferDuplicateCheck.test.js +4 -0
- package/test/unit/models/transfer/transferError.test.js +5 -1
- package/test/unit/models/transfer/transferErrorDuplicateCheck.test.js +4 -0
- package/test/unit/models/transfer/transferExtension.test.js +26 -9
- package/test/unit/models/transfer/transferFulfilment.test.js +14 -5
- package/test/unit/models/transfer/transferFulfilmentDuplicateCheck.test.js +4 -0
- package/test/unit/models/transfer/transferParticipant.test.js +14 -5
- package/test/unit/models/transfer/transferStateChange.test.js +3 -0
- package/test/unit/models/transfer/transferTimeout.test.js +6 -0
- package/test/unit/seeds/amountType.test.js +6 -22
- package/test/unit/seeds/balanceOfPayments.test.js +6 -22
- package/test/unit/seeds/bulkProcessingState.test.js +84 -0
- package/test/unit/{lib/sidecar/nullClient.test.js → seeds/bulkTransferState.test.js} +36 -39
- package/test/unit/seeds/currency.test.js +6 -22
- package/test/unit/seeds/endpointType.test.js +6 -22
- package/test/unit/seeds/ledgerAccountType.test.js +5 -22
- package/test/unit/seeds/ledgerEntryType.test.js +6 -22
- package/test/unit/seeds/participant.test.js +5 -22
- package/test/unit/seeds/participantLimitType.test.js +6 -22
- package/test/unit/seeds/partyIdentifierType.test.js +6 -22
- package/test/unit/seeds/partyType.test.js +6 -22
- package/test/unit/seeds/settlementState.test.js +5 -22
- package/test/unit/seeds/settlementWindow1State.test.js +5 -22
- package/test/unit/seeds/transactionInitiator.test.js +6 -22
- package/test/unit/seeds/transactionInitiatorType.test.js +6 -22
- package/test/unit/seeds/transactionScenario.test.js +6 -22
- package/test/unit/seeds/transferParticipantRoleType.test.js +6 -22
- package/test/unit/seeds/transferState.test.js +6 -22
- package/test/unit/shared/plugins.test.js +31 -1
- package/test/unit/shared/setup.test.js +25 -36
- package/test/util/helpers.js +37 -2
- package/test/util/randomTransfers.js +1 -1
- package/test/util/scripts/env.sh +6 -5
- package/test/util/scripts/populateTestData.sh +204 -181
- package/test/util/scripts/populateTestDataForLegacySimulator.sh +261 -0
- package/test/util/scripts/restartObjStore.sh +1 -1
- package/test-integration.Dockerfile +15 -15
- package/test.Dockerfile +12 -12
- package/.circleci/_set_up_deploy_envs.sh +0 -47
- package/.dockerignore +0 -17
- package/.ncurc.json +0 -3
- package/server.sh +0 -4
- package/sonar-project.properties +0 -17
- package/src/lib/sidecar/index.js +0 -47
- package/src/lib/sidecar/nullClient.js +0 -18
- package/test/.env +0 -8
- package/test/integration-config.json +0 -367
- package/test/integration-runner.env +0 -31
- package/test/integration-runner.sh +0 -264
- package/test/spec-runner.sh +0 -132
- package/test/unit/lib/sidecar/index.test.js +0 -148
- 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 =
|
|
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.
|
|
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
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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.
|
|
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 =
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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, `
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
372
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
}
|