@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
@@ -0,0 +1,68 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+
21
+ * Vijaya Kumar Guthi <vijaya.guthi@infitx.com>
22
+ --------------
23
+ ******/
24
+
25
+ 'use strict'
26
+
27
+ const Db = require('../../lib/db')
28
+ const Logger = require('@mojaloop/central-services-logger')
29
+ const Enum = require('@mojaloop/central-services-shared').Enum
30
+ const TS = Enum.Transfers.TransferInternalState
31
+
32
+ const cleanup = async () => {
33
+ Logger.isDebugEnabled && Logger.debug('cleanup fxTransferTimeout')
34
+ try {
35
+ const knex = await Db.getKnex()
36
+
37
+ const ttIdList = await Db.from('fxTransferTimeout').query(async (builder) => {
38
+ const b = await builder
39
+ .whereIn('tsc.transferStateId', [`${TS.RECEIVED_FULFIL}`, `${TS.COMMITTED}`, `${TS.FAILED}`, `${TS.RESERVED_TIMEOUT}`,
40
+ `${TS.RECEIVED_REJECT}`, `${TS.EXPIRED_PREPARED}`, `${TS.EXPIRED_RESERVED}`, `${TS.ABORTED_REJECTED}`, `${TS.ABORTED_ERROR}`])
41
+ .innerJoin(
42
+ knex('fxTransferTimeout AS tt1')
43
+ .select('tsc1.commitRequestId')
44
+ .max('tsc1.fxTransferStateChangeId AS maxFxTransferStateChangeId')
45
+ .innerJoin('fxTransferStateChange AS tsc1', 'tsc1.commitRequestId', 'tt1.commitRequestId')
46
+ .groupBy('tsc1.commitRequestId').as('ts'), 'ts.commitRequestId', 'fxTransferTimeout.commitRequestId'
47
+ )
48
+ .innerJoin('fxTransferStateChange AS tsc', 'tsc.fxTransferStateChangeId', 'ts.maxFxTransferStateChangeId')
49
+ .select('fxTransferTimeout.fxTransferTimeoutId')
50
+ return b
51
+ })
52
+
53
+ await Db.from('fxTransferTimeout').query(async (builder) => {
54
+ const b = await builder
55
+ .whereIn('fxTransferTimeoutId', ttIdList.map(elem => elem.fxTransferTimeoutId))
56
+ .del()
57
+ return b
58
+ })
59
+ return ttIdList
60
+ } catch (err) {
61
+ Logger.isErrorEnabled && Logger.error(err)
62
+ throw err
63
+ }
64
+ }
65
+
66
+ module.exports = {
67
+ cleanup
68
+ }
@@ -0,0 +1,15 @@
1
+ const duplicateCheck = require('./duplicateCheck')
2
+ const fxTransfer = require('./fxTransfer')
3
+ const stateChange = require('./stateChange')
4
+ const watchList = require('./watchList')
5
+ const fxTransferTimeout = require('./fxTransferTimeout')
6
+ const fxTransferError = require('./fxTransferError')
7
+
8
+ module.exports = {
9
+ duplicateCheck,
10
+ fxTransfer,
11
+ stateChange,
12
+ watchList,
13
+ fxTransferTimeout,
14
+ fxTransferError
15
+ }
@@ -0,0 +1,47 @@
1
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
2
+ const TransferError = require('../../models/transfer/transferError')
3
+ const Db = require('../../lib/db')
4
+ const { TABLE_NAMES } = require('../../shared/constants')
5
+ const { logger } = require('../../shared/logger')
6
+
7
+ const table = TABLE_NAMES.fxTransferStateChange
8
+
9
+ const getByCommitRequestId = async (id) => {
10
+ return await Db.from(table).query(async (builder) => {
11
+ return builder
12
+ .where({ 'fxTransferStateChange.commitRequestId': id })
13
+ .select('fxTransferStateChange.*')
14
+ .orderBy('fxTransferStateChangeId', 'desc')
15
+ .first()
16
+ })
17
+ }
18
+
19
+ const logTransferError = async (id, errorCode, errorDescription) => {
20
+ try {
21
+ const stateChange = await getByCommitRequestId(id)
22
+ // todo: check if stateChange is not null
23
+ return TransferError.insert(id, stateChange.fxTransferStateChangeId, errorCode, errorDescription)
24
+ } catch (err) {
25
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
26
+ }
27
+ }
28
+
29
+ const getLatest = async () => {
30
+ try {
31
+ return await Db.from('fxTransferStateChange').query(async (builder) => {
32
+ return builder
33
+ .select('fxTransferStateChangeId')
34
+ .orderBy('fxTransferStateChangeId', 'desc')
35
+ .first()
36
+ })
37
+ } catch (err) {
38
+ logger.error('getLatest::fxTransferStateChange', err)
39
+ throw err
40
+ }
41
+ }
42
+
43
+ module.exports = {
44
+ getByCommitRequestId,
45
+ logTransferError,
46
+ getLatest
47
+ }
@@ -0,0 +1,49 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+ Contributors
9
+ --------------
10
+ This is the official list of the Mojaloop project contributors for this file.
11
+ Names of the original copyright holders (individuals or organizations)
12
+ should be listed with a '*' in the first column. People who have
13
+ contributed from an organization can be listed under the organization
14
+ that actually holds the copyright for their contributions (see the
15
+ Gates Foundation organization for an example). Those individuals should have
16
+ their names indented and be marked with a '-'. Email address can be added
17
+ optionally within square brackets <email>.
18
+ * Gates Foundation
19
+ - Name Surname <name.surname@gatesfoundation.com>
20
+ * Vijay Kumar Guthi <vijaya.guthi@infitx.com>
21
+ --------------
22
+ ******/
23
+
24
+ 'use strict'
25
+
26
+ const Db = require('../../lib/db')
27
+ const { TABLE_NAMES } = require('../../shared/constants')
28
+ const { logger } = require('../../shared/logger')
29
+
30
+ const getItemInWatchListByCommitRequestId = async (commitRequestId) => {
31
+ logger.debug(`get item in watch list (commitRequestId=${commitRequestId})`)
32
+ return Db.from(TABLE_NAMES.fxWatchList).findOne({ commitRequestId })
33
+ }
34
+
35
+ const getItemsInWatchListByDeterminingTransferId = async (determiningTransferId) => {
36
+ logger.debug(`get item in watch list (determiningTransferId=${determiningTransferId})`)
37
+ return Db.from(TABLE_NAMES.fxWatchList).find({ determiningTransferId })
38
+ }
39
+
40
+ const addToWatchList = async (record) => {
41
+ logger.debug('add to fx watch list', record)
42
+ return Db.from(TABLE_NAMES.fxWatchList).insert(record)
43
+ }
44
+
45
+ module.exports = {
46
+ getItemInWatchListByCommitRequestId,
47
+ getItemsInWatchListByDeterminingTransferId,
48
+ addToWatchList
49
+ }
@@ -35,25 +35,19 @@ const ErrorHandler = require('@mojaloop/central-services-error-handling')
35
35
  exports.getLedgerAccountByName = async (name, trx = null) => {
36
36
  try {
37
37
  const knex = Db.getKnex()
38
- const trxFunction = async (trx, doCommit = true) => {
38
+ const trxFunction = async (trx) => {
39
39
  try {
40
40
  const ledgerAccountType = await knex('ledgerAccountType')
41
41
  .select()
42
42
  .where('name', name)
43
43
  .transacting(trx)
44
- if (doCommit) {
45
- await trx.commit
46
- }
47
44
  return ledgerAccountType.length > 0 ? ledgerAccountType[0] : null
48
45
  } catch (err) {
49
- if (doCommit) {
50
- await trx.rollback
51
- }
52
46
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
53
47
  }
54
48
  }
55
49
  if (trx) {
56
- return trxFunction(trx, false)
50
+ return trxFunction(trx)
57
51
  } else {
58
52
  return knex.transaction(trxFunction)
59
53
  }
@@ -66,25 +60,19 @@ exports.getLedgerAccountByName = async (name, trx = null) => {
66
60
  exports.getLedgerAccountsByName = async (names, trx = null) => {
67
61
  try {
68
62
  const knex = Db.getKnex()
69
- const trxFunction = async (trx, doCommit = true) => {
63
+ const trxFunction = async (trx) => {
70
64
  try {
71
65
  const ledgerAccountTypes = await knex('ledgerAccountType')
72
66
  .select('name')
73
67
  .whereIn('name', names)
74
68
  .transacting(trx)
75
- if (doCommit) {
76
- await trx.commit
77
- }
78
69
  return ledgerAccountTypes
79
70
  } catch (err) {
80
- if (doCommit) {
81
- await trx.rollback
82
- }
83
71
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
84
72
  }
85
73
  }
86
74
  if (trx) {
87
- return trxFunction(trx, false)
75
+ return trxFunction(trx)
88
76
  } else {
89
77
  return knex.transaction(trxFunction)
90
78
  }
@@ -97,7 +85,7 @@ exports.getLedgerAccountsByName = async (names, trx = null) => {
97
85
  exports.bulkInsert = async (records, trx = null) => {
98
86
  try {
99
87
  const knex = Db.getKnex()
100
- const trxFunction = async (trx, doCommit = true) => {
88
+ const trxFunction = async (trx) => {
101
89
  try {
102
90
  await knex('ledgerAccountType')
103
91
  .insert(records)
@@ -107,19 +95,13 @@ exports.bulkInsert = async (records, trx = null) => {
107
95
  .from('ledgerAccountType')
108
96
  .whereIn('name', recordsNames)
109
97
  .transacting(trx)
110
- if (doCommit) {
111
- await trx.commit
112
- }
113
98
  return createdIds.map(record => record.ledgerAccountTypeId)
114
99
  } catch (err) {
115
- if (doCommit) {
116
- await trx.rollback
117
- }
118
100
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
119
101
  }
120
102
  }
121
103
  if (trx) {
122
- return trxFunction(trx, false)
104
+ return trxFunction(trx)
123
105
  } else {
124
106
  return knex.transaction(trxFunction)
125
107
  }
@@ -131,7 +113,7 @@ exports.bulkInsert = async (records, trx = null) => {
131
113
  exports.create = async (name, description, isActive, isSettleable, trx = null) => {
132
114
  try {
133
115
  const knex = Db.getKnex()
134
- const trxFunction = async (trx, doCommit = true) => {
116
+ const trxFunction = async (trx) => {
135
117
  try {
136
118
  await knex('ledgerAccountType')
137
119
  .insert({
@@ -145,19 +127,13 @@ exports.create = async (name, description, isActive, isSettleable, trx = null) =
145
127
  .from('ledgerAccountType')
146
128
  .where('name', name)
147
129
  .transacting(trx)
148
- if (doCommit) {
149
- await trx.commit
150
- }
151
130
  return createdId[0].ledgerAccountTypeId
152
131
  } catch (err) {
153
- if (doCommit) {
154
- await trx.rollback()
155
- }
156
132
  throw ErrorHandler.Factory.reformatFSPIOPError(err)
157
133
  }
158
134
  }
159
135
  if (trx) {
160
- return trxFunction(trx, false)
136
+ return trxFunction(trx)
161
137
  } else {
162
138
  return knex.transaction(trxFunction)
163
139
  }
@@ -26,7 +26,6 @@
26
26
 
27
27
  const Db = require('../../lib/db')
28
28
  const ErrorHandler = require('@mojaloop/central-services-error-handling')
29
- // const Logger = require('@mojaloop/central-services-logger')
30
29
 
31
30
  const getByParams = async (params) => {
32
31
  try {
@@ -0,0 +1,96 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+
9
+ Contributors
10
+ --------------
11
+ This is the official list of the Mojaloop project contributors for this file.
12
+ Names of the original copyright holders (individuals or organizations)
13
+ should be listed with a '*' in the first column. People who have
14
+ contributed from an organization can be listed under the organization
15
+ that actually holds the copyright for their contributions (see the
16
+ Gates Foundation organization for an example). Those individuals should have
17
+ their names indented and be marked with a '-'. Email address can be added
18
+ optionally within square brackets <email>.
19
+ * Gates Foundation
20
+ - Name Surname <name.surname@gatesfoundation.com>
21
+
22
+ * Eugen Klymniuk <eugen.klymniuk@infitx.com>
23
+ --------------
24
+ **********/
25
+
26
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
27
+ const Db = require('../../lib/db')
28
+ const { logger } = require('../../shared/logger')
29
+ const { TABLE_NAMES, DB_ERROR_CODES } = require('../../shared/constants')
30
+
31
+ const TABLE = TABLE_NAMES.externalParticipant
32
+ const ID_FIELD = 'externalParticipantId'
33
+
34
+ const log = logger.child(`DB#${TABLE}`)
35
+
36
+ const create = async ({ name, proxyId }) => {
37
+ try {
38
+ const result = await Db.from(TABLE).insert({ name, proxyId })
39
+ log.debug('create result:', { result })
40
+ return result
41
+ } catch (err) {
42
+ if (err.code === DB_ERROR_CODES.duplicateEntry) {
43
+ log.warn('duplicate entry for externalParticipant. Skip inserting', { name, proxyId })
44
+ return null
45
+ }
46
+ log.error('error in create', err)
47
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
48
+ }
49
+ }
50
+
51
+ const getAll = async (options = {}) => {
52
+ try {
53
+ const result = await Db.from(TABLE).find({}, options)
54
+ log.debug('getAll result:', { result })
55
+ return result
56
+ } catch (err) /* istanbul ignore next */ {
57
+ log.error('error in getAll:', err)
58
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
59
+ }
60
+ }
61
+
62
+ const getOneBy = async (criteria, options) => {
63
+ try {
64
+ const result = await Db.from(TABLE).findOne(criteria, options)
65
+ log.debug('getOneBy result:', { criteria, result })
66
+ return result
67
+ } catch (err) /* istanbul ignore next */ {
68
+ log.error('error in getOneBy:', err)
69
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
70
+ }
71
+ }
72
+ const getById = async (id, options = {}) => getOneBy({ [ID_FIELD]: id }, options)
73
+ const getByName = async (name, options = {}) => getOneBy({ name }, options)
74
+
75
+ const destroyBy = async (criteria) => {
76
+ try {
77
+ const result = await Db.from(TABLE).destroy(criteria)
78
+ log.debug('destroyBy result:', { criteria, result })
79
+ return result
80
+ } catch (err) /* istanbul ignore next */ {
81
+ log.error('error in destroyBy', err)
82
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
83
+ }
84
+ }
85
+ const destroyById = async (id) => destroyBy({ [ID_FIELD]: id })
86
+ const destroyByName = async (name) => destroyBy({ name })
87
+
88
+ // todo: think, if we need update method
89
+ module.exports = {
90
+ create,
91
+ getAll,
92
+ getById,
93
+ getByName,
94
+ destroyById,
95
+ destroyByName
96
+ }
@@ -0,0 +1,148 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2017 Bill & Melinda Gates Foundation
5
+ The Mojaloop files are made available by the Bill & Melinda Gates Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8
+
9
+ Contributors
10
+ --------------
11
+ This is the official list of the Mojaloop project contributors for this file.
12
+ Names of the original copyright holders (individuals or organizations)
13
+ should be listed with a '*' in the first column. People who have
14
+ contributed from an organization can be listed under the organization
15
+ that actually holds the copyright for their contributions (see the
16
+ Gates Foundation organization for an example). Those individuals should have
17
+ their names indented and be marked with a '-'. Email address can be added
18
+ optionally within square brackets <email>.
19
+ * Gates Foundation
20
+ - Name Surname <name.surname@gatesfoundation.com>
21
+
22
+ * Eugen Klymniuk <eugen.klymniuk@infitx.com>
23
+ --------------
24
+ **********/
25
+
26
+ const ErrorHandler = require('@mojaloop/central-services-error-handling')
27
+ const Metrics = require('@mojaloop/central-services-metrics')
28
+ const cache = require('../../lib/cache')
29
+ const externalParticipantModel = require('./externalParticipant')
30
+
31
+ let cacheClient
32
+ let epAllCacheKey
33
+
34
+ const buildUnifiedCachedData = (allExternalParticipants) => {
35
+ // build indexes - optimization for byId and byName access
36
+ const indexById = {}
37
+ const indexByName = {}
38
+
39
+ allExternalParticipants.forEach(({ createdDate, ...ep }) => {
40
+ indexById[ep.externalParticipantId] = ep
41
+ indexByName[ep.name] = ep
42
+ })
43
+
44
+ // build unified structure - indexes + data
45
+ return {
46
+ indexById,
47
+ indexByName,
48
+ allExternalParticipants
49
+ }
50
+ }
51
+
52
+ const getExternalParticipantsCached = async () => {
53
+ const queryName = 'model_getExternalParticipantsCached'
54
+ const histTimer = Metrics.getHistogram(
55
+ 'model_externalParticipant',
56
+ `${queryName} - Metrics for externalParticipant model`,
57
+ ['success', 'queryName', 'hit']
58
+ ).startTimer()
59
+
60
+ let cachedParticipants = cacheClient.get(epAllCacheKey)
61
+ const hit = !!cachedParticipants
62
+
63
+ if (!cachedParticipants) {
64
+ const allParticipants = await externalParticipantModel.getAll()
65
+ cachedParticipants = buildUnifiedCachedData(allParticipants)
66
+ cacheClient.set(epAllCacheKey, cachedParticipants)
67
+ } else {
68
+ // unwrap participants list from catbox structure
69
+ cachedParticipants = cachedParticipants.item
70
+ }
71
+ histTimer({ success: true, queryName, hit })
72
+
73
+ return cachedParticipants
74
+ }
75
+
76
+ /*
77
+ Public API
78
+ */
79
+ const initialize = () => {
80
+ /* Register as cache client */
81
+ const cacheClientMeta = {
82
+ id: 'externalParticipants',
83
+ preloadCache: getExternalParticipantsCached
84
+ }
85
+
86
+ cacheClient = cache.registerCacheClient(cacheClientMeta)
87
+ epAllCacheKey = cacheClient.createKey('all')
88
+ }
89
+
90
+ const invalidateCache = async () => {
91
+ cacheClient.drop(epAllCacheKey)
92
+ }
93
+
94
+ const getById = async (id) => {
95
+ try {
96
+ const cachedParticipants = await getExternalParticipantsCached()
97
+ return cachedParticipants.indexById[id]
98
+ } catch (err) /* istanbul ignore next */ {
99
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
100
+ }
101
+ }
102
+
103
+ const getByName = async (name) => {
104
+ try {
105
+ const cachedParticipants = await getExternalParticipantsCached()
106
+ return cachedParticipants.indexByName[name]
107
+ } catch (err) /* istanbul ignore next */ {
108
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
109
+ }
110
+ }
111
+
112
+ const getAll = async () => {
113
+ try {
114
+ const cachedParticipants = await getExternalParticipantsCached()
115
+ return cachedParticipants.allExternalParticipants
116
+ } catch (err) /* istanbul ignore next */ {
117
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
118
+ }
119
+ }
120
+
121
+ const withInvalidate = (theFunctionName) => {
122
+ return async (...args) => {
123
+ try {
124
+ const result = await externalParticipantModel[theFunctionName](...args)
125
+ await invalidateCache()
126
+ return result
127
+ } catch (err) /* istanbul ignore next */ {
128
+ throw ErrorHandler.Factory.reformatFSPIOPError(err)
129
+ }
130
+ }
131
+ }
132
+
133
+ const create = withInvalidate('create')
134
+ const destroyById = withInvalidate('destroyById')
135
+ const destroyByName = withInvalidate('destroyByName')
136
+
137
+ module.exports = {
138
+ initialize,
139
+ invalidateCache,
140
+
141
+ getAll,
142
+ getById,
143
+ getByName,
144
+
145
+ create,
146
+ destroyById,
147
+ destroyByName
148
+ }