@mojaloop/central-ledger 15.3.0-snapshot.2 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/CODEOWNERS +1 -1
- package/audit-ci.jsonc +2 -1
- package/package.json +9 -4
- package/seeds/bulkProcessingState.js +15 -0
- package/seeds/bulkTransferState.js +5 -0
- package/src/domain/bulkTransfer/index.js +12 -1
- package/src/handlers/bulk/fulfil/handler.js +65 -12
- package/src/handlers/bulk/get/handler.js +3 -7
- package/src/handlers/bulk/prepare/handler.js +28 -7
- package/src/handlers/bulk/processing/handler.js +15 -3
- package/src/handlers/bulk/shared/validator.js +93 -18
- package/src/lib/config.js +1 -1
- package/src/models/bulkTransfer/bulkTransfer.js +21 -7
- package/src/models/bulkTransfer/facade.js +45 -1
- package/audit-resolve.json +0 -156
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [16.2.0](https://github.com/mojaloop/central-ledger/compare/v16.1.0...v16.2.0) (2022-08-15)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **mojaloop/#2801:** add fulfil timestamp validation and more error handling ([#916](https://github.com/mojaloop/central-ledger/issues/916)) ([336a0a2](https://github.com/mojaloop/central-ledger/commit/336a0a27e908eedeb0dcf8b171ad8c0edfb4c3d8)), closes [mojaloop/#2801](https://github.com/mojaloop/project/issues/2801)
|
|
11
|
+
|
|
12
|
+
## [16.1.0](https://github.com/mojaloop/central-ledger/compare/v16.0.0...v16.1.0) (2022-08-11)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **mojaloop/#2796:** duplicate transaction not getting callback for post /bulkTransfers (not forked) ([#915](https://github.com/mojaloop/central-ledger/issues/915)) ([e520aa5](https://github.com/mojaloop/central-ledger/commit/e520aa5c5e748a051534f69f5734918f0e02f379)), closes [mojaloop/#2796](https://github.com/mojaloop/project/issues/2796)
|
|
18
|
+
|
|
19
|
+
## [16.0.0](https://github.com/mojaloop/central-ledger/compare/v15.2.0...v16.0.0) (2022-08-05)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### ⚠ BREAKING CHANGES
|
|
23
|
+
|
|
24
|
+
* rework bulk handler validation (#913)
|
|
25
|
+
|
|
26
|
+
### Refactors
|
|
27
|
+
|
|
28
|
+
* rework bulk handler validation ([#913](https://github.com/mojaloop/central-ledger/issues/913)) ([38d29fd](https://github.com/mojaloop/central-ledger/commit/38d29fde13128fa656b6a3cfd71c05ba52b92994))
|
|
29
|
+
|
|
5
30
|
## [15.2.0](https://github.com/mojaloop/central-ledger/compare/v15.1.3...v15.2.0) (2022-08-01)
|
|
6
31
|
|
|
7
32
|
|
package/CODEOWNERS
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## @global-owner1 and @global-owner2 will be requested for
|
|
6
6
|
## review when someone opens a pull request.
|
|
7
7
|
#* @global-owner1 @global-owner2
|
|
8
|
-
*
|
|
8
|
+
* @mdebarros @elnyry-sam-k @vijayg10 @kleyow
|
|
9
9
|
## Order is important; the last matching pattern takes the most
|
|
10
10
|
## precedence. When someone opens a pull request that only
|
|
11
11
|
## modifies JS files, only @js-owner and not the global
|
package/audit-ci.jsonc
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojaloop/central-ledger",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.2.0",
|
|
4
4
|
"description": "Central ledger hosted by a scheme to record and settle transfers",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ModusBox",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": "=16.x"
|
|
28
28
|
},
|
|
29
|
+
"imports": {
|
|
30
|
+
"#src/*": "./src/*.js",
|
|
31
|
+
"#test/*": "./test/*.js"
|
|
32
|
+
},
|
|
29
33
|
"pre-commit": [
|
|
30
34
|
"lint",
|
|
31
35
|
"dep:check",
|
|
@@ -62,6 +66,7 @@
|
|
|
62
66
|
"docker:down": "docker-compose -f docker-compose.yml down -v",
|
|
63
67
|
"docker:clean": "docker-compose -f docker-compose.yml down --rmi local",
|
|
64
68
|
"generate-docs": "npx jsdoc -c jsdoc.json",
|
|
69
|
+
"audit:fix": "npm audit fix",
|
|
65
70
|
"audit:check": "npx audit-ci --config ./audit-ci.jsonc",
|
|
66
71
|
"dep:check": "npx ncu -e 2",
|
|
67
72
|
"dep:update": "npx ncu -u",
|
|
@@ -80,7 +85,7 @@
|
|
|
80
85
|
"@mojaloop/central-services-health": "14.0.1",
|
|
81
86
|
"@mojaloop/central-services-logger": "11.0.1",
|
|
82
87
|
"@mojaloop/central-services-metrics": "12.0.5",
|
|
83
|
-
"@mojaloop/central-services-shared": "17.0
|
|
88
|
+
"@mojaloop/central-services-shared": "17.3.0",
|
|
84
89
|
"@mojaloop/central-services-stream": "11.0.0",
|
|
85
90
|
"@mojaloop/event-sdk": "11.0.2",
|
|
86
91
|
"@mojaloop/ml-number": "11.2.1",
|
|
@@ -93,7 +98,7 @@
|
|
|
93
98
|
"catbox-memory": "4.0.1",
|
|
94
99
|
"commander": "9.4.0",
|
|
95
100
|
"cron": "2.1.0",
|
|
96
|
-
"decimal.js": "10.
|
|
101
|
+
"decimal.js": "10.4.0",
|
|
97
102
|
"docdash": "1.2.0",
|
|
98
103
|
"event-stream": "4.0.1",
|
|
99
104
|
"five-bells-condition": "5.0.1",
|
|
@@ -107,7 +112,7 @@
|
|
|
107
112
|
"moment": "2.29.4",
|
|
108
113
|
"rc": "1.2.8",
|
|
109
114
|
"require-glob": "^4.1.0",
|
|
110
|
-
"uuid4": "2.0.
|
|
115
|
+
"uuid4": "2.0.3"
|
|
111
116
|
},
|
|
112
117
|
"optionalDependencies": {
|
|
113
118
|
"mysql": "2.18.1"
|
|
@@ -27,44 +27,59 @@
|
|
|
27
27
|
|
|
28
28
|
const bulkProcessingStates = [
|
|
29
29
|
{
|
|
30
|
+
bulkProcessingStateId: 1,
|
|
30
31
|
name: 'RECEIVED',
|
|
31
32
|
description: 'The switch has received the individual transfer ids part of the bulk transfer'
|
|
32
33
|
},
|
|
33
34
|
{
|
|
35
|
+
bulkProcessingStateId: 2,
|
|
34
36
|
name: 'RECEIVED_DUPLICATE',
|
|
35
37
|
description: 'The switch has matched individual transfer as duplicate'
|
|
36
38
|
},
|
|
37
39
|
{
|
|
40
|
+
bulkProcessingStateId: 3,
|
|
38
41
|
name: 'RECEIVED_INVALID',
|
|
39
42
|
description: 'The switch has matched individual transfer as invalid within Prepare or Position Handler'
|
|
40
43
|
},
|
|
41
44
|
{
|
|
45
|
+
bulkProcessingStateId: 4,
|
|
42
46
|
name: 'ACCEPTED',
|
|
43
47
|
description: 'The switch has reserved the funds for the transfers in the bulk'
|
|
44
48
|
},
|
|
45
49
|
{
|
|
50
|
+
bulkProcessingStateId: 5,
|
|
46
51
|
name: 'PROCESSING',
|
|
47
52
|
description: 'Fulfilment request has been received for the individual transfer'
|
|
48
53
|
},
|
|
49
54
|
{
|
|
55
|
+
bulkProcessingStateId: 6,
|
|
50
56
|
name: 'FULFIL_DUPLICATE',
|
|
51
57
|
description: 'The switch has matched individual transfer fulfil as duplicate'
|
|
52
58
|
},
|
|
53
59
|
{
|
|
60
|
+
bulkProcessingStateId: 7,
|
|
54
61
|
name: 'FULFIL_INVALID',
|
|
55
62
|
description: 'The switch has matched individual transfer fulfilment as invalid within Fulfil or Position Handler'
|
|
56
63
|
},
|
|
57
64
|
{
|
|
65
|
+
bulkProcessingStateId: 8,
|
|
58
66
|
name: 'COMPLETED',
|
|
59
67
|
description: 'The switch has marked the individual transfer as committed'
|
|
60
68
|
},
|
|
61
69
|
{
|
|
70
|
+
bulkProcessingStateId: 9,
|
|
62
71
|
name: 'REJECTED',
|
|
63
72
|
description: 'The switch has marked the individual transfer as rejected'
|
|
64
73
|
},
|
|
65
74
|
{
|
|
75
|
+
bulkProcessingStateId: 10,
|
|
66
76
|
name: 'EXPIRED',
|
|
67
77
|
description: 'The switch has marked the individual transfer as timed out'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
bulkProcessingStateId: 11,
|
|
81
|
+
name: 'ABORTING',
|
|
82
|
+
description: 'The switch has marked the individual transfer as aborting due to failed validation'
|
|
68
83
|
}
|
|
69
84
|
]
|
|
70
85
|
|
|
@@ -80,6 +80,11 @@ const bulkTransferStates = [
|
|
|
80
80
|
bulkTransferStateId: 'INVALID',
|
|
81
81
|
enumeration: 'REJECTED',
|
|
82
82
|
description: 'Final state when the switch has completed processing of pending invalid bulk transfer'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
bulkTransferStateId: 'ABORTING',
|
|
86
|
+
enumeration: 'PROCESSING',
|
|
87
|
+
description: 'The switch is attempting to abort all individual transfers'
|
|
83
88
|
}
|
|
84
89
|
]
|
|
85
90
|
|
|
@@ -126,7 +126,8 @@ const getBulkTransferById = async (id) => {
|
|
|
126
126
|
expiration: bulkTransfer.expirationDate,
|
|
127
127
|
completedDate: bulkTransfer.completedTimestamp,
|
|
128
128
|
payerBulkTransfer,
|
|
129
|
-
payeeBulkTransfer
|
|
129
|
+
payeeBulkTransfer,
|
|
130
|
+
bulkTransferStateEnumeration: bulkTransfer.bulkTransferStateEnumeration
|
|
130
131
|
}
|
|
131
132
|
} catch (err) {
|
|
132
133
|
Logger.isErrorEnabled && Logger.error(err)
|
|
@@ -162,9 +163,19 @@ const getBulkTransferExtensionListById = async (id, completedTimestamp) => {
|
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
const bulkFulfilTransitionToAborting = async (bulkFulfilPayload, stateReason = null) => {
|
|
167
|
+
try {
|
|
168
|
+
BulkTransferFacade.saveBulkTransferAborting(bulkFulfilPayload, stateReason)
|
|
169
|
+
} catch (err) {
|
|
170
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
171
|
+
throw err
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
165
175
|
const BulkTransferService = {
|
|
166
176
|
getBulkTransferById,
|
|
167
177
|
getBulkTransferExtensionListById,
|
|
178
|
+
bulkFulfilTransitionToAborting,
|
|
168
179
|
getBulkTransferByTransferId: BulkTransferModel.getByTransferId,
|
|
169
180
|
getParticipantsById: BulkTransferModel.getParticipantsById,
|
|
170
181
|
bulkPrepare: BulkTransferFacade.saveBulkTransferReceived,
|
|
@@ -144,14 +144,36 @@ const bulkFulfil = async (error, messages) => {
|
|
|
144
144
|
const bulkTransfers = await BulkTransferService.getBulkTransferById(payload.bulkTransferId)
|
|
145
145
|
for (const individualTransferFulfil of bulkTransfers.payeeBulkTransfer.individualTransferResults) {
|
|
146
146
|
individualTransferFulfil.errorInformation = payload.errorInformation
|
|
147
|
-
await sendIndividualTransfer(
|
|
147
|
+
await sendIndividualTransfer(
|
|
148
|
+
message,
|
|
149
|
+
messageId,
|
|
150
|
+
kafkaTopic,
|
|
151
|
+
headers,
|
|
152
|
+
payload,
|
|
153
|
+
state,
|
|
154
|
+
params,
|
|
155
|
+
individualTransferFulfil,
|
|
156
|
+
histTimerEnd,
|
|
157
|
+
Enum.Transfers.BulkProcessingState.PROCESSING
|
|
158
|
+
)
|
|
148
159
|
}
|
|
149
160
|
} else {
|
|
150
161
|
const IndividualTransferFulfilModel = BulkTransferModels.getIndividualTransferFulfilModel()
|
|
151
162
|
|
|
152
163
|
// enable async/await operations for the stream
|
|
153
164
|
for await (const doc of IndividualTransferFulfilModel.find({ messageId }).cursor()) {
|
|
154
|
-
await sendIndividualTransfer(
|
|
165
|
+
await sendIndividualTransfer(
|
|
166
|
+
message,
|
|
167
|
+
messageId,
|
|
168
|
+
kafkaTopic,
|
|
169
|
+
headers,
|
|
170
|
+
payload,
|
|
171
|
+
state,
|
|
172
|
+
params,
|
|
173
|
+
doc.payload,
|
|
174
|
+
histTimerEnd,
|
|
175
|
+
Enum.Transfers.BulkProcessingState.PROCESSING
|
|
176
|
+
)
|
|
155
177
|
}
|
|
156
178
|
}
|
|
157
179
|
} catch (err) { // TODO: handle individual transfers streaming error
|
|
@@ -162,7 +184,27 @@ const bulkFulfil = async (error, messages) => {
|
|
|
162
184
|
}
|
|
163
185
|
} else {
|
|
164
186
|
Logger.isErrorEnabled && Logger.error(Util.breadcrumb(location, { path: 'validationFailed' }))
|
|
165
|
-
|
|
187
|
+
|
|
188
|
+
const validationFspiopError = reasons.shift()
|
|
189
|
+
if (reasons.length > 0) {
|
|
190
|
+
validationFspiopError.extensions = []
|
|
191
|
+
// If there are multiple validation errors attach them as extensions
|
|
192
|
+
// to the first error
|
|
193
|
+
reasons.forEach((reason, i) => {
|
|
194
|
+
validationFspiopError.extensions.push({
|
|
195
|
+
key: `additionalErrors${i}`,
|
|
196
|
+
value: reason.message
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
// Converting FSPIOPErrors to strings is verbose, so we reduce the errors
|
|
201
|
+
// to just their message.
|
|
202
|
+
const reasonsMessages = reasons.map(function (reason) {
|
|
203
|
+
return reason.message
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
Logger.isErrorEnabled && Logger.error(`validationFailure Reasons - ${JSON.stringify(reasonsMessages)}`)
|
|
207
|
+
|
|
166
208
|
try {
|
|
167
209
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'saveInvalidRequest'))
|
|
168
210
|
/**
|
|
@@ -171,12 +213,24 @@ const bulkFulfil = async (error, messages) => {
|
|
|
171
213
|
* reason is "FSPIOP-Source header should match Payee". In this case we should not
|
|
172
214
|
* abort the bulk as we would have accepted non-legitimate source.
|
|
173
215
|
*/
|
|
174
|
-
const state = await BulkTransferService.
|
|
216
|
+
const state = await BulkTransferService.bulkFulfilTransitionToAborting(payload)
|
|
175
217
|
const bulkTransfers = await BulkTransferService.getBulkTransferById(payload.bulkTransferId)
|
|
176
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, 'Bulk fulfil failed validation')
|
|
177
218
|
for (const individualTransferFulfil of bulkTransfers.payeeBulkTransfer.individualTransferResults) {
|
|
178
|
-
individualTransferFulfil.errorInformation =
|
|
179
|
-
|
|
219
|
+
individualTransferFulfil.errorInformation = validationFspiopError.toApiErrorObject().errorInformation
|
|
220
|
+
// Abort-Reject all individual transfers
|
|
221
|
+
// The bulk processing handler will handle informing the payer
|
|
222
|
+
await sendIndividualTransfer(
|
|
223
|
+
message,
|
|
224
|
+
messageId,
|
|
225
|
+
kafkaTopic,
|
|
226
|
+
headers,
|
|
227
|
+
payload,
|
|
228
|
+
state,
|
|
229
|
+
params,
|
|
230
|
+
individualTransferFulfil,
|
|
231
|
+
histTimerEnd,
|
|
232
|
+
Enum.Transfers.BulkProcessingState.ABORTING
|
|
233
|
+
)
|
|
180
234
|
}
|
|
181
235
|
} catch (err) {
|
|
182
236
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInternal2--${actionLetter}7`))
|
|
@@ -191,12 +245,11 @@ const bulkFulfil = async (error, messages) => {
|
|
|
191
245
|
}
|
|
192
246
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorGeneric--${actionLetter}8`))
|
|
193
247
|
|
|
194
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, reasons.toString())
|
|
195
248
|
const eventDetail = { functionality: Enum.Events.Event.Type.NOTIFICATION, action }
|
|
196
249
|
params.message.value.content.uriParams = { id: bulkTransferId }
|
|
197
250
|
|
|
198
|
-
await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError:
|
|
199
|
-
throw
|
|
251
|
+
await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: validationFspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
|
|
252
|
+
throw validationFspiopError
|
|
200
253
|
}
|
|
201
254
|
} catch (err) {
|
|
202
255
|
Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--BP0`)
|
|
@@ -212,13 +265,13 @@ const bulkFulfil = async (error, messages) => {
|
|
|
212
265
|
* @async
|
|
213
266
|
* @description sends individual transfers to the fulfil handler
|
|
214
267
|
*/
|
|
215
|
-
const sendIndividualTransfer = async (message, messageId, kafkaTopic, headers, payload, state, params, individualTransferFulfil, histTimerEnd) => {
|
|
268
|
+
const sendIndividualTransfer = async (message, messageId, kafkaTopic, headers, payload, state, params, individualTransferFulfil, histTimerEnd, bulkProcessingStateId) => {
|
|
216
269
|
const transferId = individualTransferFulfil.transferId
|
|
217
270
|
delete individualTransferFulfil.transferId
|
|
218
271
|
const bulkTransferAssociationRecord = {
|
|
219
272
|
transferId,
|
|
220
273
|
bulkTransferId: payload.bulkTransferId,
|
|
221
|
-
bulkProcessingStateId
|
|
274
|
+
bulkProcessingStateId,
|
|
222
275
|
errorCode: payload.errorInformation ? payload.errorInformation.errorCode : undefined,
|
|
223
276
|
errorDescription: payload.errorInformation ? payload.errorInformation.errorDescription : undefined
|
|
224
277
|
}
|
|
@@ -115,15 +115,11 @@ const getBulkTransfer = async (error, messages) => {
|
|
|
115
115
|
const bulkTransferResult = await BulkTransferService.getBulkTransferById(bulkTransferId)
|
|
116
116
|
const bulkTransfer = isPayeeRequest ? bulkTransferResult.payeeBulkTransfer : bulkTransferResult.payerBulkTransfer
|
|
117
117
|
let payload = {
|
|
118
|
-
bulkTransferState:
|
|
118
|
+
bulkTransferState: bulkTransferResult.bulkTransferStateEnumeration
|
|
119
119
|
}
|
|
120
120
|
let fspiopError
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
errorInformation: bulkTransfer.individualTransferResults[0].errorInformation
|
|
124
|
-
}
|
|
125
|
-
fspiopError = ErrorHandler.Factory.createFSPIOPErrorFromErrorInformation(payload.errorInformation)
|
|
126
|
-
} else if (bulkTransfer.bulkTransferState !== Enum.Transfers.BulkTransferState.PROCESSING) {
|
|
121
|
+
|
|
122
|
+
if (bulkTransfer.bulkTransferState !== Enum.Transfers.BulkTransferState.PROCESSING) {
|
|
127
123
|
payload = {
|
|
128
124
|
...payload,
|
|
129
125
|
completedTimestamp: bulkTransfer.completedTimestamp,
|
|
@@ -96,6 +96,7 @@ const bulkPrepare = async (error, messages) => {
|
|
|
96
96
|
const headers = message.value.content.headers
|
|
97
97
|
const action = message.value.metadata.event.action
|
|
98
98
|
const bulkTransferId = payload.bulkTransferId
|
|
99
|
+
const bulkTransferHash = payload.hash
|
|
99
100
|
const kafkaTopic = message.topic
|
|
100
101
|
|
|
101
102
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { method: 'bulkPrepare' }))
|
|
@@ -105,9 +106,12 @@ const bulkPrepare = async (error, messages) => {
|
|
|
105
106
|
|
|
106
107
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, { path: 'dupCheck' }))
|
|
107
108
|
|
|
108
|
-
const { hasDuplicateId, hasDuplicateHash } = await Comparators.duplicateCheckComparator(bulkTransferId,
|
|
109
|
+
const { hasDuplicateId, hasDuplicateHash } = await Comparators.duplicateCheckComparator(bulkTransferId, bulkTransferHash, BulkTransferService.getBulkTransferDuplicateCheck, BulkTransferService.saveBulkTransferDuplicateCheck, {
|
|
110
|
+
hashOverride: true
|
|
111
|
+
})
|
|
112
|
+
|
|
109
113
|
if (hasDuplicateId && hasDuplicateHash) {
|
|
110
|
-
const eventDetail = { functionality: Enum.Events.Event.Type.NOTIFICATION, action: TransferEventAction.
|
|
114
|
+
const eventDetail = { functionality: Enum.Events.Event.Type.NOTIFICATION, action: TransferEventAction.BULK_PREPARE_DUPLICATE }
|
|
111
115
|
const bulkTransferResult = await BulkTransferService.getBulkTransferById(bulkTransferId)
|
|
112
116
|
const bulkTransfer = bulkTransferResult.payerBulkTransfer
|
|
113
117
|
const transferStateEnum = bulkTransfer && bulkTransfer.bulkTransferState
|
|
@@ -222,11 +226,29 @@ const bulkPrepare = async (error, messages) => {
|
|
|
222
226
|
}
|
|
223
227
|
} else { // handle validation failure
|
|
224
228
|
Logger.isErrorEnabled && Logger.error(Util.breadcrumb(location, { path: 'validationFailed' }))
|
|
225
|
-
|
|
229
|
+
const validationFspiopError = reasons.shift()
|
|
230
|
+
if (reasons.length > 0) {
|
|
231
|
+
validationFspiopError.extensions = []
|
|
232
|
+
// If there are multiple validation errors attach them as extensions
|
|
233
|
+
// to the first error
|
|
234
|
+
reasons.forEach((reason, i) => {
|
|
235
|
+
validationFspiopError.extensions.push({
|
|
236
|
+
key: `additionalErrors${i}`,
|
|
237
|
+
value: reason.message
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
// Converting FSPIOPErrors to strings is verbose, so we reduce the errors
|
|
242
|
+
// to just their message.
|
|
243
|
+
// `bulkTransferStateChange.reason` also has a 512 character limit.
|
|
244
|
+
const reasonsMessages = reasons.map(function (reason) {
|
|
245
|
+
return reason.message
|
|
246
|
+
})
|
|
247
|
+
Logger.isErrorEnabled && Logger.error(`validationFailure Reasons - ${JSON.stringify(reasonsMessages)}`)
|
|
226
248
|
|
|
227
249
|
try { // save invalid request for auditing
|
|
228
250
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, 'saveInvalidRequest'))
|
|
229
|
-
await BulkTransferService.bulkPrepare(payload, { payerParticipantId, payeeParticipantId },
|
|
251
|
+
await BulkTransferService.bulkPrepare(payload, { payerParticipantId, payeeParticipantId }, reasonsMessages.toString(), false)
|
|
230
252
|
} catch (err) { // handle insert error and produce error callback notification to payer
|
|
231
253
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorInternal2--${actionLetter}6`))
|
|
232
254
|
Logger.isErrorEnabled && Logger.error(err)
|
|
@@ -241,12 +263,11 @@ const bulkPrepare = async (error, messages) => {
|
|
|
241
263
|
// produce validation error callback notification to payer
|
|
242
264
|
Logger.isInfoEnabled && Logger.info(Util.breadcrumb(location, `callbackErrorGeneric--${actionLetter}7`))
|
|
243
265
|
|
|
244
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR, reasons.toString())
|
|
245
266
|
const eventDetail = { functionality: Enum.Events.Event.Type.NOTIFICATION, action }
|
|
246
267
|
params.message.value.content.uriParams = { id: bulkTransferId }
|
|
247
268
|
|
|
248
|
-
await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError:
|
|
249
|
-
throw
|
|
269
|
+
await Kafka.proceed(Config.KAFKA_CONFIG, params, { consumerCommit, fspiopError: validationFspiopError.toApiErrorObject(Config.ERROR_HANDLING), eventDetail, fromSwitch })
|
|
270
|
+
throw validationFspiopError
|
|
250
271
|
}
|
|
251
272
|
} catch (err) {
|
|
252
273
|
Logger.isErrorEnabled && Logger.error(`${Util.breadcrumb(location)}::${err.message}--BP0`)
|
|
@@ -150,7 +150,7 @@ const bulkProcessing = async (error, messages) => {
|
|
|
150
150
|
errorCode = payload.errorInformation && payload.errorInformation.errorCode
|
|
151
151
|
errorDescription = payload.errorInformation && payload.errorInformation.errorDescription
|
|
152
152
|
} else {
|
|
153
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action for bulk in ${Enum.Transfers.BulkTransferState.RECEIVED} state`)
|
|
153
|
+
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action ${action} for bulk in ${Enum.Transfers.BulkTransferState.RECEIVED} state`)
|
|
154
154
|
throw fspiopError
|
|
155
155
|
}
|
|
156
156
|
} else if ([Enum.Transfers.BulkTransferState.ACCEPTED].includes(bulkTransferInfo.bulkTransferStateId)) {
|
|
@@ -162,7 +162,7 @@ const bulkProcessing = async (error, messages) => {
|
|
|
162
162
|
errorCode = payload.errorInformation && payload.errorInformation.errorCode
|
|
163
163
|
errorDescription = payload.errorInformation && payload.errorInformation.errorDescription
|
|
164
164
|
} else {
|
|
165
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action for bulk in ${Enum.Transfers.BulkTransferState.ACCEPTED} state`)
|
|
165
|
+
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action ${action} for bulk in ${Enum.Transfers.BulkTransferState.ACCEPTED} state`)
|
|
166
166
|
throw fspiopError
|
|
167
167
|
}
|
|
168
168
|
} else if ([Enum.Transfers.BulkTransferState.PROCESSING, Enum.Transfers.BulkTransferState.PENDING_FULFIL, Enum.Transfers.BulkTransferState.EXPIRING].includes(bulkTransferInfo.bulkTransferStateId)) {
|
|
@@ -190,7 +190,19 @@ const bulkProcessing = async (error, messages) => {
|
|
|
190
190
|
errorCode = payload.errorInformation && payload.errorInformation.errorCode
|
|
191
191
|
errorDescription = payload.errorInformation && payload.errorInformation.errorDescription
|
|
192
192
|
} else {
|
|
193
|
-
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action for bulk in ${Enum.Transfers.BulkTransferState.PROCESSING} state`)
|
|
193
|
+
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action ${action} for bulk in ${Enum.Transfers.BulkTransferState.PROCESSING} state`)
|
|
194
|
+
throw fspiopError
|
|
195
|
+
}
|
|
196
|
+
} else if ([Enum.Transfers.BulkTransferState.ABORTING].includes(bulkTransferInfo.bulkTransferStateId)) {
|
|
197
|
+
if (action === Enum.Events.Event.Action.BULK_ABORT) {
|
|
198
|
+
criteriaState = Enum.Transfers.BulkTransferState.ABORTING
|
|
199
|
+
processingStateId = Enum.Transfers.BulkProcessingState.FULFIL_INVALID
|
|
200
|
+
completedBulkState = Enum.Transfers.BulkTransferState.REJECTED
|
|
201
|
+
incompleteBulkState = Enum.Transfers.BulkTransferState.ABORTING
|
|
202
|
+
errorCode = payload.errorInformation && payload.errorInformation.errorCode
|
|
203
|
+
errorDescription = payload.errorInformation && payload.errorInformation.errorDescription
|
|
204
|
+
} else {
|
|
205
|
+
const fspiopError = ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.INTERNAL_SERVER_ERROR, `Invalid action ${action} for bulk in ${Enum.Transfers.BulkTransferState.ABORTING} state`)
|
|
194
206
|
throw fspiopError
|
|
195
207
|
}
|
|
196
208
|
} else if (bulkTransferInfo.bulkTransferStateId === Enum.Transfers.BulkTransferState.COMPLETED && action === Enum.Events.Event.Action.FULFIL_DUPLICATE) {
|
|
@@ -38,56 +38,104 @@ const Participant = require('../../../domain/participant')
|
|
|
38
38
|
const BulkTransferService = require('../../../domain/bulkTransfer')
|
|
39
39
|
const Config = require('../../../lib/config')
|
|
40
40
|
const Enum = require('@mojaloop/central-services-shared').Enum
|
|
41
|
+
const ErrorHandler = require('@mojaloop/central-services-error-handling')
|
|
41
42
|
|
|
42
43
|
const reasons = []
|
|
43
|
-
const defaultLagSeconds = 300
|
|
44
44
|
|
|
45
45
|
const validateDifferentFsp = (payload) => {
|
|
46
46
|
const isPayerAndPayeeDifferent = (payload.payerFsp.toLowerCase() !== payload.payeeFsp.toLowerCase())
|
|
47
47
|
if (!isPayerAndPayeeDifferent) {
|
|
48
|
-
reasons.push(
|
|
48
|
+
reasons.push(
|
|
49
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
50
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
51
|
+
'Payer and Payee FSPs should be different'
|
|
52
|
+
)
|
|
53
|
+
)
|
|
49
54
|
return false
|
|
50
55
|
}
|
|
51
56
|
return true
|
|
52
57
|
}
|
|
58
|
+
|
|
53
59
|
const validateExpiration = (payload) => {
|
|
54
60
|
if (Date.parse(payload.expiration) < Date.parse(new Date().toDateString())) {
|
|
55
|
-
reasons.push(
|
|
61
|
+
reasons.push(
|
|
62
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
63
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
64
|
+
`Expiration date ${new Date(payload.expiration.toString()).toISOString()} is already in the past`
|
|
65
|
+
)
|
|
66
|
+
)
|
|
56
67
|
return false
|
|
57
68
|
}
|
|
58
69
|
return true
|
|
59
70
|
}
|
|
71
|
+
|
|
60
72
|
const validateFspiopSourceMatchesPayer = (payload, headers) => {
|
|
61
73
|
const matched = (headers && headers[Enum.Http.Headers.FSPIOP.SOURCE] === payload.payerFsp)
|
|
62
74
|
if (!matched) {
|
|
63
|
-
reasons.push(
|
|
75
|
+
reasons.push(
|
|
76
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
77
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
78
|
+
'FSPIOP-Source header should match Payer FSP'
|
|
79
|
+
)
|
|
80
|
+
)
|
|
64
81
|
return false
|
|
65
82
|
}
|
|
66
83
|
return true
|
|
67
84
|
}
|
|
85
|
+
|
|
68
86
|
const validateFspiopSourceAndDestination = async (payload, headers) => {
|
|
69
87
|
const participant = await BulkTransferService.getParticipantsById(payload.bulkTransferId)
|
|
70
88
|
const matchedPayee = (headers && headers[Enum.Http.Headers.FSPIOP.SOURCE] === participant.payeeFsp)
|
|
71
89
|
const matchedPayer = (headers && headers[Enum.Http.Headers.FSPIOP.DESTINATION] === participant.payerFsp)
|
|
72
90
|
if (!matchedPayee) {
|
|
73
|
-
reasons.push(
|
|
91
|
+
reasons.push(
|
|
92
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
93
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
94
|
+
'FSPIOP-Source header should match Payee FSP'
|
|
95
|
+
)
|
|
96
|
+
)
|
|
74
97
|
return false
|
|
75
98
|
}
|
|
76
99
|
if (!matchedPayer) {
|
|
77
|
-
reasons.push(
|
|
100
|
+
reasons.push(
|
|
101
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
102
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
103
|
+
'FSPIOP-Destination header should match Payer FSP'
|
|
104
|
+
)
|
|
105
|
+
)
|
|
78
106
|
return false
|
|
79
107
|
}
|
|
80
108
|
return true
|
|
81
109
|
}
|
|
82
|
-
|
|
110
|
+
|
|
111
|
+
const validateParticipantByName = async (participantName, isPayer = null) => {
|
|
112
|
+
let fspiopErrorCode
|
|
113
|
+
if (isPayer == null) {
|
|
114
|
+
fspiopErrorCode = ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR
|
|
115
|
+
} else if (isPayer) {
|
|
116
|
+
fspiopErrorCode = ErrorHandler.Enums.FSPIOPErrorCodes.PAYER_FSP_ID_NOT_FOUND
|
|
117
|
+
} else {
|
|
118
|
+
fspiopErrorCode = ErrorHandler.Enums.FSPIOPErrorCodes.PAYEE_FSP_ID_NOT_FOUND
|
|
119
|
+
}
|
|
120
|
+
|
|
83
121
|
const participant = await Participant.getByName(participantName)
|
|
84
122
|
let isValid = false
|
|
85
123
|
let participantId
|
|
86
124
|
if (!participant) {
|
|
87
|
-
reasons.push(
|
|
125
|
+
reasons.push(
|
|
126
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
127
|
+
fspiopErrorCode,
|
|
128
|
+
`Participant ${participantName} not found`
|
|
129
|
+
)
|
|
130
|
+
)
|
|
88
131
|
} else if (!participant.isActive) {
|
|
89
132
|
participantId = participant.participantId
|
|
90
|
-
reasons.push(
|
|
133
|
+
reasons.push(
|
|
134
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
135
|
+
fspiopErrorCode,
|
|
136
|
+
`Participant ${participantName} is inactive`
|
|
137
|
+
)
|
|
138
|
+
)
|
|
91
139
|
} else {
|
|
92
140
|
participantId = participant.participantId
|
|
93
141
|
isValid = true
|
|
@@ -101,7 +149,12 @@ const validateBulkTransfer = async (payload, headers) => {
|
|
|
101
149
|
let payerParticipantId = null
|
|
102
150
|
let payeeParticipantId = null
|
|
103
151
|
if (!payload) {
|
|
104
|
-
reasons.push(
|
|
152
|
+
reasons.push(
|
|
153
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
154
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
155
|
+
'A valid Bulk transfer message must be provided'
|
|
156
|
+
)
|
|
157
|
+
)
|
|
105
158
|
isValid = false
|
|
106
159
|
return { isValid, reasons, payerParticipantId, payeeParticipantId }
|
|
107
160
|
}
|
|
@@ -109,7 +162,7 @@ const validateBulkTransfer = async (payload, headers) => {
|
|
|
109
162
|
isValid = isValid && validateExpiration(payload)
|
|
110
163
|
isValid = isValid && validateDifferentFsp(payload)
|
|
111
164
|
if (isValid) {
|
|
112
|
-
const result = await validateParticipantByName(payload.payerFsp)
|
|
165
|
+
const result = await validateParticipantByName(payload.payerFsp, true)
|
|
113
166
|
isValid = result.isValid
|
|
114
167
|
payerParticipantId = result.participantId
|
|
115
168
|
}
|
|
@@ -122,9 +175,16 @@ const validateBulkTransfer = async (payload, headers) => {
|
|
|
122
175
|
// and body may be different. So we can not enforce that check.
|
|
123
176
|
|
|
124
177
|
// We validate that both these fspId's exist and and continue on.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
178
|
+
// We check the body then the header only if the body is valid to avoid
|
|
179
|
+
// adding two errors to `reasons`
|
|
180
|
+
const payloadResult = await validateParticipantByName(payload.payeeFsp, false)
|
|
181
|
+
isValid = payloadResult.isValid
|
|
182
|
+
|
|
183
|
+
if (isValid) {
|
|
184
|
+
const headerResult = await validateParticipantByName(headers[Enum.Http.Headers.FSPIOP.DESTINATION], false)
|
|
185
|
+
isValid = headerResult.isValid
|
|
186
|
+
}
|
|
187
|
+
|
|
128
188
|
payeeParticipantId = payloadResult.participantId
|
|
129
189
|
}
|
|
130
190
|
return { isValid, reasons, payerParticipantId, payeeParticipantId }
|
|
@@ -134,7 +194,12 @@ const validateBulkTransferFulfilment = async (payload, headers) => {
|
|
|
134
194
|
reasons.length = 0
|
|
135
195
|
let isValid = true
|
|
136
196
|
if (!payload) {
|
|
137
|
-
reasons.push(
|
|
197
|
+
reasons.push(
|
|
198
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
199
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
200
|
+
'A valid Bulk transfer fulfilment message must be provided'
|
|
201
|
+
)
|
|
202
|
+
)
|
|
138
203
|
isValid = false
|
|
139
204
|
return { isValid, reasons }
|
|
140
205
|
}
|
|
@@ -144,14 +209,24 @@ const validateBulkTransferFulfilment = async (payload, headers) => {
|
|
|
144
209
|
}
|
|
145
210
|
|
|
146
211
|
const validateCompletedTimestamp = (payload) => {
|
|
147
|
-
const maxLag =
|
|
212
|
+
const maxLag = Config.MAX_FULFIL_TIMEOUT_DURATION_SECONDS * 1000
|
|
148
213
|
const completedTimestamp = new Date(payload.completedTimestamp)
|
|
149
214
|
const now = new Date()
|
|
150
215
|
if (completedTimestamp > now) {
|
|
151
|
-
reasons.push(
|
|
216
|
+
reasons.push(
|
|
217
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
218
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
219
|
+
'Bulk fulfil failed validation - completedTimestamp fails because future timestamp was provided'
|
|
220
|
+
)
|
|
221
|
+
)
|
|
152
222
|
return false
|
|
153
223
|
} else if (completedTimestamp < now - maxLag) {
|
|
154
|
-
reasons.push(
|
|
224
|
+
reasons.push(
|
|
225
|
+
ErrorHandler.Factory.createFSPIOPError(
|
|
226
|
+
ErrorHandler.Enums.FSPIOPErrorCodes.VALIDATION_ERROR,
|
|
227
|
+
'Bulk fulfil failed validation - completedTimestamp fails because provided timestamp exceeded the maximum timeout duration'
|
|
228
|
+
)
|
|
229
|
+
)
|
|
155
230
|
return false
|
|
156
231
|
}
|
|
157
232
|
return true
|
package/src/lib/config.js
CHANGED
|
@@ -3,7 +3,7 @@ const RC = require('rc')('CLEDG', require('../../config/default.json'))
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
HOSTNAME: RC.HOSTNAME.replace(/\/$/, ''),
|
|
5
5
|
PORT: RC.PORT,
|
|
6
|
-
MAX_FULFIL_TIMEOUT_DURATION_SECONDS: RC.MAX_FULFIL_TIMEOUT_DURATION_SECONDS,
|
|
6
|
+
MAX_FULFIL_TIMEOUT_DURATION_SECONDS: RC.MAX_FULFIL_TIMEOUT_DURATION_SECONDS || 300,
|
|
7
7
|
MONGODB_URI: RC.MONGODB.URI,
|
|
8
8
|
MONGODB_DEBUG: (RC.MONGODB.DEBUG === true || RC.MONGODB.DEBUG === 'true'),
|
|
9
9
|
MONGODB_DISABLED: RC.MONGODB.DISABLED,
|
|
@@ -34,11 +34,19 @@ const getById = async (id) => {
|
|
|
34
34
|
.innerJoin('participant AS payee', 'payee.participantId', 'bulkTransfer.payeeParticipantId')
|
|
35
35
|
.innerJoin('bulkTransferStateChange AS btsc', 'btsc.bulkTransferId', 'bulkTransfer.bulkTransferId')
|
|
36
36
|
.leftJoin('bulkTransferFulfilment AS btf', 'btf.bulkTransferId', 'bulkTransfer.bulkTransferId')
|
|
37
|
+
.leftJoin('bulkTransferState AS bts', 'bts.bulkTransferStateId', 'btsc.bulkTransferStateId')
|
|
37
38
|
.where({ 'bulkTransfer.bulkTransferId': id })
|
|
38
39
|
.orderBy('btsc.bulkTransferStateChangeId', 'desc')
|
|
39
|
-
.select(
|
|
40
|
-
'
|
|
41
|
-
|
|
40
|
+
.select(
|
|
41
|
+
'bulkTransfer.bulkTransferId',
|
|
42
|
+
'btsc.bulkTransferStateId',
|
|
43
|
+
'btf.completedDate AS completedTimestamp',
|
|
44
|
+
'bts.enumeration AS bulkTransferStateEnumeration',
|
|
45
|
+
'payer.name AS payerFsp',
|
|
46
|
+
'payee.name AS payeeFsp',
|
|
47
|
+
'bulkTransfer.bulkQuoteId',
|
|
48
|
+
'bulkTransfer.expirationDate'
|
|
49
|
+
).first()
|
|
42
50
|
return result
|
|
43
51
|
})
|
|
44
52
|
} catch (err) {
|
|
@@ -55,13 +63,19 @@ const getByTransferId = async (id) => {
|
|
|
55
63
|
.innerJoin('participant AS payer', 'payer.participantId', 'bulkTransfer.payerParticipantId')
|
|
56
64
|
.innerJoin('participant AS payee', 'payee.participantId', 'bulkTransfer.payeeParticipantId')
|
|
57
65
|
.innerJoin('bulkTransferStateChange AS btsc', 'btsc.bulkTransferId', 'bulkTransfer.bulkTransferId')
|
|
66
|
+
.leftJoin('bulkTransferState AS bts', 'bts.bulkTransferStateId', 'btsc.bulkTransferStateId')
|
|
58
67
|
.leftJoin('bulkTransferFulfilment AS btf', 'btf.bulkTransferId', 'bulkTransfer.bulkTransferId')
|
|
59
68
|
.where({ 'bta.transferId': id })
|
|
60
69
|
.orderBy('btsc.bulkTransferStateChangeId', 'desc')
|
|
61
|
-
.select(
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
|
|
70
|
+
.select(
|
|
71
|
+
'bulkTransfer.bulkTransferId',
|
|
72
|
+
'btsc.bulkTransferStateId',
|
|
73
|
+
'btf.completedDate AS completedTimestamp',
|
|
74
|
+
'bts.enumeration AS bulkTransferStateEnumeration',
|
|
75
|
+
'payer.name AS payerFsp',
|
|
76
|
+
'payee.name AS payeeFsp',
|
|
77
|
+
'bulkTransfer.bulkQuoteId'
|
|
78
|
+
).first()
|
|
65
79
|
return result
|
|
66
80
|
})
|
|
67
81
|
} catch (err) {
|
|
@@ -172,10 +172,54 @@ const saveBulkTransferErrorProcessing = async (payload, stateReason = null, isVa
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
const saveBulkTransferAborting = async (payload, stateReason = null) => {
|
|
176
|
+
try {
|
|
177
|
+
const bulkTransferFulfilmentRecord = {
|
|
178
|
+
bulkTransferId: payload.bulkTransferId,
|
|
179
|
+
completedDate: Time.getUTCString(new Date(payload.completedTimestamp))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const state = Enum.Transfers.BulkTransferState.ABORTING
|
|
183
|
+
const bulkTransferStateChangeRecord = {
|
|
184
|
+
bulkTransferId: payload.bulkTransferId,
|
|
185
|
+
bulkTransferStateId: state,
|
|
186
|
+
reason: stateReason
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const knex = await Db.getKnex()
|
|
190
|
+
return await knex.transaction(async (trx) => {
|
|
191
|
+
try {
|
|
192
|
+
await knex('bulkTransferFulfilment').transacting(trx).insert(bulkTransferFulfilmentRecord)
|
|
193
|
+
if (payload.extensionList && payload.extensionList.extension) {
|
|
194
|
+
const bulkTransferExtensionsRecordList = payload.extensionList.extension.map(ext => {
|
|
195
|
+
return {
|
|
196
|
+
bulkTransferId: payload.bulkTransferId,
|
|
197
|
+
isFulfilment: true,
|
|
198
|
+
key: ext.key,
|
|
199
|
+
value: ext.value
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
await knex.batchInsert('bulkTransferExtension', bulkTransferExtensionsRecordList).transacting(trx)
|
|
203
|
+
}
|
|
204
|
+
await knex('bulkTransferStateChange').transacting(trx).insert(bulkTransferStateChangeRecord)
|
|
205
|
+
await trx.commit
|
|
206
|
+
return state
|
|
207
|
+
} catch (err) {
|
|
208
|
+
await trx.rollback
|
|
209
|
+
throw err
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
} catch (err) {
|
|
213
|
+
Logger.isErrorEnabled && Logger.error(err)
|
|
214
|
+
throw err
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
175
218
|
const TransferFacade = {
|
|
176
219
|
saveBulkTransferReceived,
|
|
177
220
|
saveBulkTransferProcessing,
|
|
178
|
-
saveBulkTransferErrorProcessing
|
|
221
|
+
saveBulkTransferErrorProcessing,
|
|
222
|
+
saveBulkTransferAborting
|
|
179
223
|
}
|
|
180
224
|
|
|
181
225
|
module.exports = TransferFacade
|
package/audit-resolve.json
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"decisions": {
|
|
3
|
-
"1075703|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs": {
|
|
4
|
-
"decision": "ignore",
|
|
5
|
-
"madeAt": 1657029291345,
|
|
6
|
-
"expiresAt": 1659621287320
|
|
7
|
-
},
|
|
8
|
-
"1075703|@mojaloop/event-sdk>grpc>protobufjs": {
|
|
9
|
-
"decision": "ignore",
|
|
10
|
-
"madeAt": 1657029291345,
|
|
11
|
-
"expiresAt": 1659621287320
|
|
12
|
-
},
|
|
13
|
-
"1075704|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs": {
|
|
14
|
-
"decision": "ignore",
|
|
15
|
-
"madeAt": 1657029292596,
|
|
16
|
-
"expiresAt": 1659621287320
|
|
17
|
-
},
|
|
18
|
-
"1075704|@mojaloop/event-sdk>grpc>protobufjs": {
|
|
19
|
-
"decision": "ignore",
|
|
20
|
-
"madeAt": 1657029292596,
|
|
21
|
-
"expiresAt": 1659621287320
|
|
22
|
-
},
|
|
23
|
-
"1070030|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>shins>markdown-it": {
|
|
24
|
-
"decision": "ignore",
|
|
25
|
-
"madeAt": 1657029293742,
|
|
26
|
-
"expiresAt": 1659621287320
|
|
27
|
-
},
|
|
28
|
-
"1070030|widdershins>markdown-it": {
|
|
29
|
-
"decision": "ignore",
|
|
30
|
-
"madeAt": 1657547585781,
|
|
31
|
-
"expiresAt": 1660139581210
|
|
32
|
-
},
|
|
33
|
-
"1068155|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>shins>markdown-it>sanitize-html": {
|
|
34
|
-
"decision": "ignore",
|
|
35
|
-
"madeAt": 1657029294926,
|
|
36
|
-
"expiresAt": 1659621287320
|
|
37
|
-
},
|
|
38
|
-
"1070260|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>shins>markdown-it>sanitize-html": {
|
|
39
|
-
"decision": "ignore",
|
|
40
|
-
"madeAt": 1657029296035,
|
|
41
|
-
"expiresAt": 1659621287320
|
|
42
|
-
},
|
|
43
|
-
"1070412|ejs": {
|
|
44
|
-
"decision": "ignore",
|
|
45
|
-
"madeAt": 1657029299037,
|
|
46
|
-
"expiresAt": 1659621287320
|
|
47
|
-
},
|
|
48
|
-
"1068386|hapi-auth-basic>hapi": {
|
|
49
|
-
"decision": "ignore",
|
|
50
|
-
"madeAt": 1657029300211,
|
|
51
|
-
"expiresAt": 1659621287320
|
|
52
|
-
},
|
|
53
|
-
"1068399|hapi-auth-basic>hapi>ammo": {
|
|
54
|
-
"decision": "ignore",
|
|
55
|
-
"madeAt": 1657029301421,
|
|
56
|
-
"expiresAt": 1659621287320
|
|
57
|
-
},
|
|
58
|
-
"1068389|hapi-auth-basic>hapi>ammo>subtext": {
|
|
59
|
-
"decision": "ignore",
|
|
60
|
-
"madeAt": 1657029302848,
|
|
61
|
-
"expiresAt": 1659621287320
|
|
62
|
-
},
|
|
63
|
-
"1068390|hapi-auth-basic>hapi>ammo>subtext": {
|
|
64
|
-
"decision": "ignore",
|
|
65
|
-
"madeAt": 1657029304128,
|
|
66
|
-
"expiresAt": 1659621287320
|
|
67
|
-
},
|
|
68
|
-
"1067553|swagger2openapi>better-ajv-errors>jsonpointer": {
|
|
69
|
-
"decision": "ignore",
|
|
70
|
-
"madeAt": 1657029305419,
|
|
71
|
-
"expiresAt": 1659621287320
|
|
72
|
-
},
|
|
73
|
-
"1067946|swagger2openapi>better-ajv-errors>jsonpointer>oas-validator>ajv": {
|
|
74
|
-
"decision": "ignore",
|
|
75
|
-
"madeAt": 1657029307042,
|
|
76
|
-
"expiresAt": 1659621287320
|
|
77
|
-
},
|
|
78
|
-
"1068310|widdershins>markdown-it>yargs>yargs-parser": {
|
|
79
|
-
"decision": "ignore",
|
|
80
|
-
"madeAt": 1657029308195,
|
|
81
|
-
"expiresAt": 1659621287320
|
|
82
|
-
},
|
|
83
|
-
"1080969|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
84
|
-
"decision": "ignore",
|
|
85
|
-
"madeAt": 1657547584804,
|
|
86
|
-
"expiresAt": 1660139581210
|
|
87
|
-
},
|
|
88
|
-
"1080969|@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
89
|
-
"decision": "ignore",
|
|
90
|
-
"madeAt": 1657547584804,
|
|
91
|
-
"expiresAt": 1660139581210
|
|
92
|
-
},
|
|
93
|
-
"1070030|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment>shins>markdown-it": {
|
|
94
|
-
"decision": "ignore",
|
|
95
|
-
"madeAt": 1657547585781,
|
|
96
|
-
"expiresAt": 1660139581210
|
|
97
|
-
},
|
|
98
|
-
"1068155|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment>shins>markdown-it>sanitize-html": {
|
|
99
|
-
"decision": "ignore",
|
|
100
|
-
"madeAt": 1657547586612,
|
|
101
|
-
"expiresAt": 1660139581210
|
|
102
|
-
},
|
|
103
|
-
"1070260|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment>shins>markdown-it>sanitize-html": {
|
|
104
|
-
"decision": "ignore",
|
|
105
|
-
"madeAt": 1657547587381,
|
|
106
|
-
"expiresAt": 1660139581210
|
|
107
|
-
},
|
|
108
|
-
"1070030|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment>widdershins>markdown-it": {
|
|
109
|
-
"decision": "ignore",
|
|
110
|
-
"madeAt": 1657548277077,
|
|
111
|
-
"expiresAt": 1660140273753
|
|
112
|
-
},
|
|
113
|
-
"1070030|shins>markdown-it": {
|
|
114
|
-
"decision": "ignore",
|
|
115
|
-
"madeAt": 1657548277077,
|
|
116
|
-
"expiresAt": 1660140273753
|
|
117
|
-
},
|
|
118
|
-
"1068310|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment>widdershins>markdown-it>yargs>yargs-parser": {
|
|
119
|
-
"decision": "ignore",
|
|
120
|
-
"madeAt": 1657548277911,
|
|
121
|
-
"expiresAt": 1660140273753
|
|
122
|
-
},
|
|
123
|
-
"1068155|shins>markdown-it>sanitize-html": {
|
|
124
|
-
"decision": "ignore",
|
|
125
|
-
"madeAt": 1657548278663,
|
|
126
|
-
"expiresAt": 1660140273753
|
|
127
|
-
},
|
|
128
|
-
"1070260|shins>markdown-it>sanitize-html": {
|
|
129
|
-
"decision": "ignore",
|
|
130
|
-
"madeAt": 1657548279466,
|
|
131
|
-
"expiresAt": 1660140273753
|
|
132
|
-
},
|
|
133
|
-
"1081008|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
134
|
-
"decision": "ignore",
|
|
135
|
-
"madeAt": 1657629511965,
|
|
136
|
-
"expiresAt": 1660221505784
|
|
137
|
-
},
|
|
138
|
-
"1081008|@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
139
|
-
"decision": "ignore",
|
|
140
|
-
"madeAt": 1657629511965,
|
|
141
|
-
"expiresAt": 1660221505784
|
|
142
|
-
},
|
|
143
|
-
"1081761|@mojaloop/central-services-shared>@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
144
|
-
"decision": "ignore",
|
|
145
|
-
"madeAt": 1658884308135,
|
|
146
|
-
"expiresAt": 1661476300260
|
|
147
|
-
},
|
|
148
|
-
"1081761|@mojaloop/event-sdk>grpc>protobufjs>moment": {
|
|
149
|
-
"decision": "ignore",
|
|
150
|
-
"madeAt": 1658884308135,
|
|
151
|
-
"expiresAt": 1661476300260
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
"rules": {},
|
|
155
|
-
"version": 1
|
|
156
|
-
}
|