diff --git a/package-lock.json b/package-lock.json index ddb134f0..54a92720 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "central-settlement", - "version": "12.0.2", + "version": "12.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5795,12 +5795,12 @@ } }, "eslint": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.19.0.tgz", - "integrity": "sha512-CGlMgJY56JZ9ZSYhJuhow61lMPPjUzWmChFya71Z/jilVos7mR/jPgaEfVGgMBY5DshbKdG8Ezb8FDCHcoMEMg==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.20.0.tgz", + "integrity": "sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", + "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -5812,7 +5812,7 @@ "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", - "esquery": "^1.2.0", + "esquery": "^1.4.0", "esutils": "^2.0.2", "file-entry-cache": "^6.0.0", "functional-red-black-tree": "^1.0.1", @@ -5917,6 +5917,21 @@ "ms": "2.1.2" } }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + }, "glob-parent": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", @@ -16765,9 +16780,9 @@ }, "dependencies": { "ajv": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.4.tgz", - "integrity": "sha512-xzzzaqgEQfmuhbhAoqjJ8T/1okb6gAzXn/eQRNpAN1AEUoHJTNF9xCDRTtf/s3SKldtZfa+RJeTs+BQq+eZ/sw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.1.0.tgz", + "integrity": "sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", diff --git a/package.json b/package.json index f154c43a..550d2d50 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "central-settlement", "description": "Central settlements hosted by a scheme to record and make settlements.", - "version": "12.0.2", + "version": "12.0.3", "license": "Apache-2.0", "private": false, "author": "ModusBox", @@ -63,7 +63,7 @@ "chai-subset": "1.6.0", "jest": "26.6.3", "jest-junit": "12.0.0", - "eslint": "7.19.0", + "eslint": "7.20.0", "faucet": "0.0.1", "get-port": "5.1.1", "node-fetch": "2.6.1", diff --git a/src/domain/transferSettlement/index.js b/src/domain/transferSettlement/index.js index 16e878ae..c3701a54 100644 --- a/src/domain/transferSettlement/index.js +++ b/src/domain/transferSettlement/index.js @@ -27,13 +27,26 @@ const ErrorHandler = require('@mojaloop/central-services-error-handling') const TransferSettlementModel = require('../../models/transferSettlement') +const Logger = require('@mojaloop/central-services-logger') module.exports = { processMsgFulfil: async function (transferEventId, transferEventStateStatus, trx) { + Logger.debug(`transferSettlement::processMsgFulfil(transferEventId=${transferEventId}, transferEventStateStatus=${transferEventStateStatus}) - start`) try { - await TransferSettlementModel.updateStateChange(transferEventId, transferEventStateStatus, trx) + // TODO: Refactor to use ENUM for settlementGranularityName = 'GROSS' function input param + // Get the 'GROSS' settlement model by transfer + const grossSettlementModel = await TransferSettlementModel.getSettlementModelByTransferId(transferEventId, 'GROSS') + Logger.debug(`transferSettlement::processMsgFulfil - result grossSettlementModel=${JSON.stringify(grossSettlementModel)}`) + Logger.debug(`transferSettlement::processMsgFulfil - grossSettlementModel.length=${grossSettlementModel.length}`) + if (grossSettlementModel.length > 0) { + Logger.debug(`transferSettlement::processMsgFulfil - updateStateChange(transferEventId=${transferEventId}, transferEventStateStatus=${transferEventStateStatus}) - start`) + await TransferSettlementModel.updateStateChange(transferEventId, transferEventStateStatus, trx) + Logger.debug('transferSettlement::processMsgFulfil - updateStateChange - end') + } + Logger.debug('transferSettlement::processMsgFulfil - end') return true } catch (err) { + Logger.debug('transferSettlement::processMsgFulfil - error!', err) throw ErrorHandler.Factory.reformatFSPIOPError(err) } }, diff --git a/src/handlers/transferSettlement/handler.js b/src/handlers/transferSettlement/handler.js index 3d814efa..41c2d749 100644 --- a/src/handlers/transferSettlement/handler.js +++ b/src/handlers/transferSettlement/handler.js @@ -114,7 +114,7 @@ async function processTransferSettlement (error, messages) { return true } } catch (err) { - Logger.error(`${Utility.breadcrumb(LOG_LOCATION)}::${err.message}--0`) + Logger.error(`${Utility.breadcrumb(LOG_LOCATION)}::${err.message}--0`, err) return true } } diff --git a/src/models/transferSettlement/facade.js b/src/models/transferSettlement/facade.js index 561cb5d9..4e72c1d4 100644 --- a/src/models/transferSettlement/facade.js +++ b/src/models/transferSettlement/facade.js @@ -52,6 +52,10 @@ async function insertLedgerEntry (ledgerEntry, transferId, trx = null) { .where('PC.currencyId', ledgerEntry.currency) .transacting(trx) + if (!Array.isArray(recordsToInsert) || recordsToInsert.length === 0) { + throw new Error(`No settlement model defined for transferId: ${transferId} and ledgerEntry: ${JSON.stringify(ledgerEntry)}`) + } + await knex('transferParticipant') .insert(recordsToInsert) .transacting(trx) @@ -296,10 +300,26 @@ async function updateTransferSettlement (transferId, status, trx = null) { } } +async function getSettlementModelByTransferId (transferId, settlementGranularityName) { + Logger.info(Utility.breadcrumb(location, { method: 'getSettlementModelByTransferId' })) + const knex = await Db.getKnex() + return knex('settlementModel') + .join('participantCurrency AS pc', function () { + this.on('pc.currencyId', 'settlementModel.currencyId') + .andOn('pc.ledgerAccountTypeId', 'settlementModel.ledgerAccountTypeId') + }) + .join('transferParticipant AS tp', 'tp.participantCurrencyId', 'pc.participantCurrencyId') + .join('settlementGranularity AS g', 'g.settlementGranularityId', 'settlementModel.settlementGranularityId') + .where('tp.transferId', transferId) + .where('g.name', settlementGranularityName) + .where('settlementModel.isActive', 1) + .select('settlementModel.*') +} const Facade = { insertLedgerEntry, insertLedgerEntries, - updateTransferSettlement + updateTransferSettlement, + getSettlementModelByTransferId } module.exports = Facade diff --git a/src/models/transferSettlement/index.js b/src/models/transferSettlement/index.js index fdcc1f6c..6d71bce3 100644 --- a/src/models/transferSettlement/index.js +++ b/src/models/transferSettlement/index.js @@ -33,5 +33,6 @@ const Facade = require('./facade') module.exports = { updateStateChange: Facade.updateTransferSettlement, getTransactionObject: Facade.getTransactionRequest, - insertLedgerEntries: Facade.insertLedgerEntries + insertLedgerEntries: Facade.insertLedgerEntries, + getSettlementModelByTransferId: Facade.getSettlementModelByTransferId } diff --git a/test/unit/domain/transferSettlement/index.test.js b/test/unit/domain/transferSettlement/index.test.js index 2506b93a..a510067b 100644 --- a/test/unit/domain/transferSettlement/index.test.js +++ b/test/unit/domain/transferSettlement/index.test.js @@ -51,6 +51,7 @@ Test('TransferSettlementService', async (transferSettlementServiceTest) => { await processFulfilTest.test('process a fulfil message', async test => { try { TransferFulfilModel.updateStateChange = sandbox.stub().returns() + TransferFulfilModel.getSettlementModelByTransferId = sandbox.stub().returns([{ name: 'CGS' }]) await TransferFulfilService.processMsgFulfil(transferEventId, transferEventStateStatus) test.ok(TransferFulfilModel.updateStateChange.withArgs(transferEventId, transferEventStateStatus).calledOnce, 'TransferFulfilModel.updateStateChange with args ... called once') test.end() @@ -61,9 +62,24 @@ Test('TransferSettlementService', async (transferSettlementServiceTest) => { } }) + await processFulfilTest.test('process a fulfil message with no matching settlement model', async test => { + try { + TransferFulfilModel.updateStateChange = sandbox.stub().returns() + TransferFulfilModel.getSettlementModelByTransferId = sandbox.stub().returns([]) + await TransferFulfilService.processMsgFulfil(transferEventId, transferEventStateStatus) + test.ok(TransferFulfilModel.updateStateChange.notCalled, 'TransferFulfilModel.updateStateChange is not called') + test.end() + } catch (err) { + Logger.error(`processFulfilTest failed with error - ${err}`) + test.fail() + test.end() + } + }) + await processFulfilTest.test('throw an exception', async test => { try { TransferFulfilModel.updateStateChange = sandbox.stub().throws(new Error('Error occurred')) + TransferFulfilModel.getSettlementModelByTransferId = sandbox.stub().returns([{ name: 'CGS' }]) await TransferFulfilService.processMsgFulfil(transferEventId, transferEventStateStatus) test.fail() test.end() diff --git a/test/unit/models/transferSettlement/facade.test.js b/test/unit/models/transferSettlement/facade.test.js index ed0f4719..c00c6f85 100644 --- a/test/unit/models/transferSettlement/facade.test.js +++ b/test/unit/models/transferSettlement/facade.test.js @@ -165,6 +165,46 @@ Test('TransferSettlement facade', async (transferSettlementTest) => { } }) + await transferSettlementTest.test('insertLedgerEntry throw error, when nothing to insert', async (test) => { + try { + sandbox.stub(Db, 'getKnex') + + knexStub.transacting.onCall(0).resolves(null) + knexStub.transacting.onCall(1).resolves(1) + knexStub.transacting.onCall(2).resolves(1) + knexStub.transacting.onCall(3).resolves(1) + + knexStub.transacting.onCall(4).resolves([{ + transferStateChangeId: 4581 + }]) + knexStub.transacting.onCall(5).resolves([ + { + participantPositionId: 130, + value: 39.37, + reservedValue: 0 + }, + { + participantPositionId: 129, + value: -39.37, + reservedValue: 0 + } + ]) + knexStub.transacting.onCall(6).resolves(1) + const knexFunc = sandbox.stub().returns(knexStub) + Object.assign(knexFunc, knexStub) + Db.getKnex.returns(knexFunc) + + const transferId = '42a874d4-82a4-4471-a3fc-3dfeb6f7cb93' + await Model.insertLedgerEntry(ledgerEntry, transferId, trxStub) + test.fail('should throw an error') + + test.end() + } catch (err) { + test.ok(err instanceof Error) + test.end() + } + }) + await transferSettlementTest.test('insertLedgerEntry when participantPosition records are not updated correctly', async (test) => { try { sandbox.stub(Db, 'getKnex') @@ -519,5 +559,55 @@ Test('TransferSettlement facade', async (transferSettlementTest) => { } }) + await transferSettlementTest.test('getSettlementModelByTransferId should', async (test) => { + try { + const transferId = '154cbf04-bac7-444d-aa66-76f66126d7f5' + const settlementGranularityName = 'GROSS' + const settlementModelResult = { + settlementModelId: 1, + name: 'CGS', + isActive: 1, + settlementGranularityId: 1, + settlementInterchangeId: 1, + settlementDelayId: 1, + currencyId: 'USD', + ledgerAccountTypeId: 1 + } + + sandbox.stub(Db, 'getKnex') + + const context = sandbox.stub() + context.on = sandbox.stub().returns({ + andOn: sandbox.stub() + }) + const join1Stub = sandbox.stub().callsArgOn(1, context) + + const knexStub = sandbox.stub() + Db.getKnex.returns(knexStub) + knexStub.returns({ + join: join1Stub.returns({ + join: sandbox.stub().returns({ + join: sandbox.stub().returns({ + where: sandbox.stub().returns({ + where: sandbox.stub().returns({ + where: sandbox.stub().returns({ + select: sandbox.stub().returns(settlementModelResult) + }) + }) + }) + }) + }) + }) + }) + const result = await Model.getSettlementModelByTransferId(transferId, settlementGranularityName) + test.deepEqual(result, settlementModelResult, 'results match') + test.end() + } catch (err) { + Logger.error(`getSettlementModelByTransferId failed with error - ${err}`) + test.fail() + test.end() + } + }) + await transferSettlementTest.end() })