@mojaloop/central-ledger 17.7.8 → 17.8.0-orb.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.ncurc.yaml +3 -1
  2. package/.nvmrc +1 -1
  3. package/.nycrc.yml +16 -1
  4. package/Dockerfile +9 -10
  5. package/README.md +39 -9
  6. package/audit-ci.jsonc +15 -2
  7. package/config/default.json +17 -1
  8. package/docker/central-ledger/default.json +9 -0
  9. package/docker/config-modifier/configs/central-ledger.js +25 -0
  10. package/docker/env.sh +15 -0
  11. package/docker/kafka/scripts/provision.sh +4 -1
  12. package/docker/ml-api-adapter/default.json +4 -1
  13. package/docker-compose.yml +90 -2
  14. package/documentation/db/erd-transfer-timeout.png +0 -0
  15. package/documentation/db/erd-transfer-timeout.txt +81 -0
  16. package/documentation/fx-implementation/README.md +48 -0
  17. package/documentation/fx-implementation/assets/fx-position-movements.drawio.svg +4 -0
  18. package/documentation/fx-implementation/assets/test-scenario.drawio.svg +4 -0
  19. package/documentation/sequence-diagrams/Handler - FX timeout.plantuml +123 -0
  20. package/documentation/sequence-diagrams/Handler - FX timeout.png +0 -0
  21. package/documentation/sequence-diagrams/Handler - timeout.plantuml +81 -0
  22. package/documentation/sequence-diagrams/Handler - timeout.png +0 -0
  23. package/documentation/state-diagrams/transfer-ML-spec-states-diagram.png +0 -0
  24. package/documentation/state-diagrams/transfer-internal-states-diagram.png +0 -0
  25. package/documentation/state-diagrams/transfer-internal-states.plantuml +75 -0
  26. package/documentation/state-diagrams/transfer-states.plantuml +13 -0
  27. package/migrations/310204_transferParticipant-participantId.js +52 -0
  28. package/migrations/310403_participantPositionChange-participantCurrencyId.js +47 -0
  29. package/migrations/310404_participantPositionChange-change.js +46 -0
  30. package/migrations/600010_fxTransferType.js +43 -0
  31. package/migrations/600011_fxTransferType-indexes.js +38 -0
  32. package/migrations/600012_fxParticipantCurrencyType.js +43 -0
  33. package/migrations/600013_fxParticipantCurrencyType-indexes.js +38 -0
  34. package/migrations/600100_fxTransferDuplicateCheck.js +42 -0
  35. package/migrations/600110_fxTransferErrorDuplicateCheck.js.js +17 -0
  36. package/migrations/600200_fxTransfer.js +51 -0
  37. package/migrations/600201_fxTransfer-indexes.js +40 -0
  38. package/migrations/600400_fxTransferStateChange.js +46 -0
  39. package/migrations/600401_fxTransferStateChange-indexes.js +40 -0
  40. package/migrations/600501_fxWatchList.js +46 -0
  41. package/migrations/600502_fxWatchList-indexes.js +40 -0
  42. package/migrations/600600_fxTransferFulfilmentDuplicateCheck.js +43 -0
  43. package/migrations/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +38 -0
  44. package/migrations/600700_fxTransferFulfilment.js +47 -0
  45. package/migrations/600701_fxTransferFulfilment-indexes.js +43 -0
  46. package/migrations/600800_fxTransferExtension.js +47 -0
  47. package/migrations/601400_fxTransferTimeout.js +43 -0
  48. package/migrations/601401_fxTransferTimeout-indexes.js +37 -0
  49. package/migrations/601500_fxTransferError.js +44 -0
  50. package/migrations/601501_fxTransferError-indexes.js +37 -0
  51. package/migrations/610200_fxTransferParticipant.js +52 -0
  52. package/migrations/610201_fxTransferParticipant-indexes.js +44 -0
  53. package/migrations/610202_fxTransferParticipant-participantId.js +52 -0
  54. package/migrations/610403_participantPositionChange-fxTransfer.js +46 -0
  55. package/migrations/910101_feature904DataMigration.js +46 -52
  56. package/migrations/910102_feature949DataMigration.js +219 -225
  57. package/migrations/950104_settlementModel-settlementAccountTypeId.js +15 -20
  58. package/migrations/950108_participantProxy.js +18 -0
  59. package/migrations/950109_fxQuote.js +19 -0
  60. package/migrations/950110_fxQuoteResponse.js +25 -0
  61. package/migrations/950111_fxQuoteError.js +23 -0
  62. package/migrations/950113_fxQuoteDuplicateCheck.js +18 -0
  63. package/migrations/950114_fxQuoteResponseDuplicateCheck.js +21 -0
  64. package/migrations/950115_fxQuoteConversionTerms.js +36 -0
  65. package/migrations/950116_fxQuoteConversionTermsExtension.js +21 -0
  66. package/migrations/950117_fxQuoteResponseConversionTerms.js +39 -0
  67. package/migrations/950118_fxQuoteResponseConversionTermsExtension.js +21 -0
  68. package/migrations/950119_fxCharge.js +27 -0
  69. package/migrations/960100_create_externalParticipant.js +47 -0
  70. package/migrations/960110_alter_transferParticipant__addFiled_externalParticipantId.js +50 -0
  71. package/migrations/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +50 -0
  72. package/package.json +27 -20
  73. package/seeds/endpointType.js +18 -0
  74. package/seeds/fxParticipantCurrencyType.js +45 -0
  75. package/seeds/fxTransferType.js +45 -0
  76. package/seeds/participant.js +2 -1
  77. package/seeds/transferParticipantRoleType.js +9 -0
  78. package/seeds/transferState.js +10 -0
  79. package/src/api/interface/swagger.json +23 -30
  80. package/src/api/participants/handler.js +6 -2
  81. package/src/api/participants/routes.js +8 -7
  82. package/src/api/root/handler.js +15 -5
  83. package/src/domain/fx/cyril.js +466 -0
  84. package/src/domain/fx/index.js +107 -0
  85. package/src/domain/participant/index.js +108 -1
  86. package/src/domain/position/abort.js +215 -0
  87. package/src/domain/position/binProcessor.js +361 -99
  88. package/src/domain/position/fulfil.js +252 -111
  89. package/src/domain/position/fx-fulfil.js +138 -0
  90. package/src/domain/position/fx-prepare.js +280 -0
  91. package/src/domain/position/fx-timeout-reserved.js +159 -0
  92. package/src/domain/position/index.js +1 -0
  93. package/src/domain/position/prepare.js +69 -49
  94. package/src/domain/position/timeout-reserved.js +162 -0
  95. package/src/domain/timeout/index.js +26 -2
  96. package/src/domain/transfer/index.js +22 -5
  97. package/src/domain/transfer/transform.js +19 -6
  98. package/src/handlers/admin/handler.js +0 -2
  99. package/src/handlers/bulk/fulfil/handler.js +5 -5
  100. package/src/handlers/bulk/get/handler.js +5 -5
  101. package/src/handlers/bulk/prepare/handler.js +9 -9
  102. package/src/handlers/bulk/processing/handler.js +8 -7
  103. package/src/handlers/bulk/shared/validator.js +1 -1
  104. package/src/handlers/positions/handler.js +23 -10
  105. package/src/handlers/positions/handlerBatch.js +54 -26
  106. package/src/handlers/register.js +2 -1
  107. package/src/handlers/timeouts/handler.js +212 -68
  108. package/src/handlers/transfers/FxFulfilService.js +387 -0
  109. package/src/handlers/transfers/createRemittanceEntity.js +106 -0
  110. package/src/handlers/transfers/dto.js +53 -0
  111. package/src/handlers/transfers/handler.js +609 -571
  112. package/src/handlers/transfers/prepare.js +572 -0
  113. package/src/handlers/transfers/validator.js +75 -14
  114. package/src/lib/cache.js +1 -1
  115. package/src/lib/config.js +6 -3
  116. package/src/lib/healthCheck/subServiceHealth.js +12 -2
  117. package/src/lib/proxyCache.js +131 -0
  118. package/src/models/bulkTransfer/facade.js +58 -82
  119. package/src/models/fxTransfer/duplicateCheck.js +153 -0
  120. package/src/models/fxTransfer/fxTransfer.js +578 -0
  121. package/src/models/fxTransfer/fxTransferError.js +53 -0
  122. package/src/models/fxTransfer/fxTransferExtension.js +41 -0
  123. package/src/models/fxTransfer/fxTransferTimeout.js +68 -0
  124. package/src/models/fxTransfer/index.js +15 -0
  125. package/src/models/fxTransfer/stateChange.js +47 -0
  126. package/src/models/fxTransfer/watchList.js +49 -0
  127. package/src/models/ledgerAccountType/ledgerAccountType.js +8 -32
  128. package/src/models/misc/segment.js +0 -1
  129. package/src/models/participant/externalParticipant.js +96 -0
  130. package/src/models/participant/externalParticipantCached.js +148 -0
  131. package/src/models/participant/facade.js +192 -123
  132. package/src/models/participant/participant.js +2 -1
  133. package/src/models/participant/participantCurrency.js +1 -1
  134. package/src/models/participant/participantPosition.js +2 -8
  135. package/src/models/position/batch.js +90 -1
  136. package/src/models/position/facade.js +5 -5
  137. package/src/models/position/participantPositionChanges.js +68 -0
  138. package/src/models/settlement/settlementModel.js +5 -23
  139. package/src/models/transfer/facade.js +806 -447
  140. package/src/shared/constants.js +52 -0
  141. package/src/shared/fspiopErrorFactory.js +131 -0
  142. package/src/shared/logger/index.js +8 -0
  143. package/src/shared/loggingPlugin.js +43 -0
  144. package/src/shared/plugins.js +6 -0
  145. package/src/shared/setup.js +10 -0
  146. package/test-integration.Dockerfile +1 -1
  147. package/test.Dockerfile +1 -1
@@ -28,6 +28,7 @@ const Config = require('../src/lib/config')
28
28
 
29
29
  const participant = [
30
30
  {
31
+ participantId: Config.HUB_ID,
31
32
  name: Config.HUB_NAME,
32
33
  description: 'Hub Operator',
33
34
  createdBy: 'seeds'
@@ -36,7 +37,7 @@ const participant = [
36
37
 
37
38
  exports.seed = async function (knex) {
38
39
  try {
39
- return await knex('participant').insert(participant).onConflict('name').ignore()
40
+ return await knex('participant').insert(participant).onConflict('id').merge()
40
41
  } catch (err) {
41
42
  console.log(`Uploading seeds for participant has failed with the following error: ${err}`)
42
43
  return -1000
@@ -20,6 +20,7 @@
20
20
 
21
21
  * Georgi Georgiev <georgi.georgiev@modusbox.com>
22
22
  * Shashikant Hirugade <shashikant.hirugade@modusbox.com>
23
+ * Vijay Kumar Guthi <vijaya.guthi@infitx.com>
23
24
  --------------
24
25
  ******/
25
26
 
@@ -45,6 +46,14 @@ const transferParticipantRoleTypes = [
45
46
  {
46
47
  name: 'DFSP_POSITION',
47
48
  description: 'Indicates the position account'
49
+ },
50
+ {
51
+ name: 'INITIATING_FSP',
52
+ description: 'Identifier for the FSP who is requesting a currency conversion'
53
+ },
54
+ {
55
+ name: 'COUNTER_PARTY_FSP',
56
+ description: 'Identifier for the FXP who is performing the currency conversion'
48
57
  }
49
58
  ]
50
59
 
@@ -41,6 +41,11 @@ const transferStates = [
41
41
  enumeration: 'RESERVED',
42
42
  description: 'The switch has reserved the transfer, and has been assigned to a settlement window.'
43
43
  },
44
+ {
45
+ transferStateId: 'RECEIVED_FULFIL_DEPENDENT',
46
+ enumeration: 'RESERVED',
47
+ description: 'The switch has reserved the fxTransfer fulfilment.'
48
+ },
44
49
  {
45
50
  transferStateId: 'COMMITTED',
46
51
  enumeration: 'COMMITTED',
@@ -95,6 +100,11 @@ const transferStates = [
95
100
  transferStateId: 'SETTLED',
96
101
  enumeration: 'SETTLED',
97
102
  description: 'The switch has settled the transfer.'
103
+ },
104
+ {
105
+ transferStateId: 'RESERVED_FORWARDED',
106
+ enumeration: 'RESERVED',
107
+ description: 'The switch has forwarded the transfer to a proxy participant'
98
108
  }
99
109
  ]
100
110
 
@@ -66,6 +66,25 @@
66
66
  "tags": [
67
67
  "participants"
68
68
  ],
69
+ "parameters": [
70
+ {
71
+ "type": ["string", "boolean", "integer", "null"],
72
+ "enum": [
73
+ false,
74
+ "0",
75
+ "false",
76
+ "",
77
+ true,
78
+ "1",
79
+ "true",
80
+ null
81
+ ],
82
+ "description": "Filter by if participant is a proxy",
83
+ "name": "isProxy",
84
+ "in": "query",
85
+ "required": false
86
+ }
87
+ ],
69
88
  "responses": {
70
89
  "default": {
71
90
  "schema": {
@@ -375,9 +394,6 @@
375
394
  "description": "Name of the participant",
376
395
  "minLength": 2,
377
396
  "maxLength": 30,
378
- "x-format": {
379
- "alphanum": true
380
- },
381
397
  "name": "name",
382
398
  "in": "path",
383
399
  "required": true
@@ -404,9 +420,6 @@
404
420
  "description": "Name of the participant",
405
421
  "minLength": 2,
406
422
  "maxLength": 30,
407
- "x-format": {
408
- "alphanum": true
409
- },
410
423
  "name": "name",
411
424
  "in": "path",
412
425
  "required": true
@@ -442,9 +455,6 @@
442
455
  "description": "Name of the participant",
443
456
  "minLength": 2,
444
457
  "maxLength": 30,
445
- "x-format": {
446
- "alphanum": true
447
- },
448
458
  "name": "name",
449
459
  "in": "path",
450
460
  "required": true
@@ -663,9 +673,6 @@
663
673
  "description": "Name of the participant",
664
674
  "minLength": 2,
665
675
  "maxLength": 30,
666
- "x-format": {
667
- "alphanum": true
668
- },
669
676
  "name": "name",
670
677
  "in": "path",
671
678
  "required": true
@@ -701,9 +708,6 @@
701
708
  "description": "Name of the participant",
702
709
  "minLength": 2,
703
710
  "maxLength": 30,
704
- "x-format": {
705
- "alphanum": true
706
- },
707
711
  "name": "name",
708
712
  "in": "path",
709
713
  "required": true
@@ -917,9 +921,6 @@
917
921
  "description": "Name of the participant",
918
922
  "minLength": 2,
919
923
  "maxLength": 30,
920
- "x-format": {
921
- "alphanum": true
922
- },
923
924
  "name": "name",
924
925
  "in": "path",
925
926
  "required": true
@@ -979,9 +980,6 @@
979
980
  "description": "Name of the participant",
980
981
  "minLength": 2,
981
982
  "maxLength": 30,
982
- "x-format": {
983
- "alphanum": true
984
- },
985
983
  "name": "name",
986
984
  "in": "path",
987
985
  "required": true
@@ -1017,9 +1015,6 @@
1017
1015
  "description": "Name of the participant",
1018
1016
  "minLength": 2,
1019
1017
  "maxLength": 30,
1020
- "x-format": {
1021
- "alphanum": true
1022
- },
1023
1018
  "name": "name",
1024
1019
  "in": "path",
1025
1020
  "required": true
@@ -1062,9 +1057,6 @@
1062
1057
  "description": "Name of the participant",
1063
1058
  "minLength": 2,
1064
1059
  "maxLength": 30,
1065
- "x-format": {
1066
- "alphanum": true
1067
- },
1068
1060
  "name": "name",
1069
1061
  "in": "path",
1070
1062
  "required": true
@@ -1109,9 +1101,6 @@
1109
1101
  "description": "Name of the participant",
1110
1102
  "minLength": 2,
1111
1103
  "maxLength": 30,
1112
- "x-format": {
1113
- "alphanum": true
1114
- },
1115
1104
  "name": "name",
1116
1105
  "in": "path",
1117
1106
  "required": true
@@ -1326,6 +1315,10 @@
1326
1315
  "description": "Currency code",
1327
1316
  "$ref" : "#/definitions/Currency"
1328
1317
 
1318
+ },
1319
+ "isProxy": {
1320
+ "type": "boolean",
1321
+ "description": "Is the participant a proxy"
1329
1322
  }
1330
1323
  },
1331
1324
  "required": [
@@ -38,7 +38,7 @@ const LocalEnum = {
38
38
  disabled: 'disabled'
39
39
  }
40
40
 
41
- const entityItem = ({ name, createdDate, isActive, currencyList }, ledgerAccountIds) => {
41
+ const entityItem = ({ name, createdDate, isActive, currencyList, isProxy }, ledgerAccountIds) => {
42
42
  const link = UrlParser.toParticipantUri(name)
43
43
  const accounts = currencyList.map((currentValue) => {
44
44
  return {
@@ -58,7 +58,8 @@ const entityItem = ({ name, createdDate, isActive, currencyList }, ledgerAccount
58
58
  links: {
59
59
  self: link
60
60
  },
61
- accounts
61
+ accounts,
62
+ isProxy
62
63
  }
63
64
  }
64
65
 
@@ -160,6 +161,9 @@ const getAll = async function (request) {
160
161
  const results = await ParticipantService.getAll()
161
162
  const ledgerAccountTypes = await Enums.getEnums('ledgerAccountType')
162
163
  const ledgerAccountIds = Util.transpose(ledgerAccountTypes)
164
+ if (request.query.isProxy) {
165
+ return results.map(record => entityItem(record, ledgerAccountIds)).filter(record => record.isProxy)
166
+ }
163
167
  return results.map(record => entityItem(record, ledgerAccountIds))
164
168
  }
165
169
 
@@ -29,7 +29,7 @@ const Joi = require('joi')
29
29
  const currencyList = require('../../../seeds/currency.js').currencyList
30
30
 
31
31
  const tags = ['api', 'participants']
32
- const nameValidator = Joi.string().alphanum().min(2).max(30).required().description('Name of the participant')
32
+ const nameValidator = Joi.string().min(2).max(30).required().description('Name of the participant')
33
33
  const currencyValidator = Joi.string().valid(...currencyList).description('Currency code')
34
34
 
35
35
  module.exports = [
@@ -49,7 +49,7 @@ module.exports = [
49
49
  tags,
50
50
  validate: {
51
51
  params: Joi.object({
52
- name: Joi.string().required().description('Participant name')
52
+ name: nameValidator
53
53
  })
54
54
  }
55
55
  }
@@ -68,7 +68,8 @@ module.exports = [
68
68
  payload: Joi.object({
69
69
  name: nameValidator,
70
70
  // password: passwordValidator,
71
- currency: currencyValidator // ,
71
+ currency: currencyValidator,
72
+ isProxy: Joi.boolean().falsy(0, '0', '').truthy(1, '1').allow(true, false, 0, 1, '0', '1', null)
72
73
  // emailAddress: Joi.string().email().required()
73
74
  })
74
75
  }
@@ -89,7 +90,7 @@ module.exports = [
89
90
  isActive: Joi.boolean().required().description('Participant isActive boolean')
90
91
  }),
91
92
  params: Joi.object({
92
- name: Joi.string().required().description('Participant name')
93
+ name: nameValidator
93
94
  })
94
95
  }
95
96
  }
@@ -239,7 +240,7 @@ module.exports = [
239
240
  type: Joi.string().required().description('Account type') // Needs a validator here
240
241
  }),
241
242
  params: Joi.object({
242
- name: Joi.string().required().description('Participant name') // nameValidator
243
+ name: nameValidator // nameValidator
243
244
  })
244
245
  }
245
246
  }
@@ -306,7 +307,7 @@ module.exports = [
306
307
  description: 'Record Funds In or Out of participant account',
307
308
  validate: {
308
309
  payload: Joi.object({
309
- transferId: Joi.string().guid().required(),
310
+ transferId: Joi.string().pattern(/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]}$|^[0-9A-HJKMNP-TV-Z]{26}$6})$/).required(),
310
311
  externalReference: Joi.string().required(),
311
312
  action: Joi.string().required().valid('recordFundsIn', 'recordFundsOutPrepareReserve').label('action is missing or not supported'),
312
313
  reason: Joi.string().required(),
@@ -344,7 +345,7 @@ module.exports = [
344
345
  params: Joi.object({
345
346
  name: nameValidator,
346
347
  id: Joi.number().integer().positive(),
347
- transferId: Joi.string().guid().required()
348
+ transferId: Joi.string().pattern(/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-7][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]}$|^[0-9A-HJKMNP-TV-Z]{26}$6})$/).required()
348
349
  })
349
350
  }
350
351
  }
@@ -30,13 +30,23 @@ const { defaultHealthHandler } = require('@mojaloop/central-services-health')
30
30
  const packageJson = require('../../../package.json')
31
31
  const {
32
32
  getSubServiceHealthDatastore,
33
- getSubServiceHealthBroker
33
+ getSubServiceHealthBroker,
34
+ getSubServiceHealthProxyCache
34
35
  } = require('../../lib/healthCheck/subServiceHealth')
36
+ const Config = require('../../lib/config')
35
37
 
36
- const healthCheck = new HealthCheck(packageJson, [
37
- getSubServiceHealthDatastore,
38
- getSubServiceHealthBroker
39
- ])
38
+ const subServiceChecks = Config.PROXY_CACHE_CONFIG?.enabled
39
+ ? [
40
+ getSubServiceHealthDatastore,
41
+ getSubServiceHealthBroker,
42
+ getSubServiceHealthProxyCache
43
+ ]
44
+ : [
45
+ getSubServiceHealthDatastore,
46
+ getSubServiceHealthBroker
47
+ ]
48
+
49
+ const healthCheck = new HealthCheck(packageJson, subServiceChecks)
40
50
 
41
51
  /**
42
52
  * @function getHealth