Skip to content

Commit

Permalink
#1207- Implement settlementModels management - GET and PUT operations (
Browse files Browse the repository at this point in the history
#588)

*    Changes for story #1207:

			- Added PUT/GET routes with Joi validations
			- Included #1211 autoPositionReset property to GET requests
			- Created handler scripts
			- Implemented DAO scripts
			- Updated Unit tests

* Fixed npm audit errors

* 1207- Implement settlementModels management - GET and PUT operations

 * Moved enums to central-services-shared
 * Updated central-services-shared version
  • Loading branch information
lazolalucas authored Feb 13, 2020
1 parent 83f6e63 commit 6324ca6
Show file tree
Hide file tree
Showing 9 changed files with 8,388 additions and 885 deletions.
8,820 changes: 7,946 additions & 874 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"@mojaloop/central-services-health": "8.3.0",
"@mojaloop/central-services-logger": "8.6.0",
"@mojaloop/central-services-metrics": "8.8.0",
"@mojaloop/central-services-shared": "9.1.1",
"@mojaloop/central-services-shared": "9.1.2",
"@mojaloop/central-services-stream": "8.7.2",
"@mojaloop/event-sdk": "8.7.0",
"@mojaloop/forensic-logging-client": "8.3.0",
Expand Down
71 changes: 69 additions & 2 deletions src/api/settlementModels/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,73 @@
const SettlementService = require('../../domain/settlement')
const Sidecar = require('../../lib/sidecar')
const ErrorHandler = require('@mojaloop/central-services-error-handling')

const Cache = require('../../lib/cache')
const Util = require('@mojaloop/central-services-shared').Util
const Enum = require('@mojaloop/central-services-shared').Enum.Settlements
const Logger = require('@mojaloop/central-services-logger')

const entityItem = ({ settlementModelId, name, isActive, settlementGranularityId, settlementInterchangeId, settlementDelayId, currencyId, requireLiquidityCheck, ledgerAccountTypeId, autoPositionReset }, ledgerAccountIds, settlementGranularityIds, settlementInterchangeIds, settlementDelayIds) => {
return {
settlementModelId,
name,
isActive: Enum.booleanType[isActive],
settlementGranularity: settlementGranularityIds[settlementGranularityId],
settlementInterchange: settlementInterchangeIds[settlementInterchangeId],
settlementDelay: settlementDelayIds[settlementDelayId],
currency: currencyId,
requireLiquidityCheck: Enum.booleanType[requireLiquidityCheck],
ledgerAccountTypeId: ledgerAccountIds[ledgerAccountTypeId],
autoPositionReset: Enum.booleanType[autoPositionReset]

}
}
const handleMissingRecord = (entity) => {
if (!entity) {
throw ErrorHandler.Factory.createFSPIOPError(ErrorHandler.Enums.FSPIOPErrorCodes.ID_NOT_FOUND, 'The requested resource could not be found.')
}
return entity
}

const getByName = async function (request) {
const entity = await SettlementService.getByName(request.params.name)
handleMissingRecord(entity)
const ledgerAccountTypes = await Cache.getEnums('ledgerAccountType')
const ledgerAccountIds = Util.transpose(ledgerAccountTypes)
const settlementGranularityIds = Util.transpose(Enum.SettlementGranularity)
const settlementInterchangeIds = Util.transpose(Enum.SettlementInterchange)
const settlementDelayIds = Util.transpose(Enum.SettlementDelay)

return entityItem(entity, ledgerAccountIds, settlementGranularityIds, settlementInterchangeIds, settlementDelayIds)
}
const getAll = async function () {
const results = await SettlementService.getAll()
const ledgerAccountTypes = await Cache.getEnums('ledgerAccountType')
const ledgerAccountIds = Util.transpose(ledgerAccountTypes)
const settlementGranularityIds = Util.transpose(Enum.SettlementGranularity)
const settlementInterchangeIds = Util.transpose(Enum.SettlementInterchange)
const settlementDelayIds = Util.transpose(Enum.SettlementDelay)
return results.map(record => entityItem(record, ledgerAccountIds, settlementGranularityIds, settlementInterchangeIds, settlementDelayIds))
}

const update = async function (request) {
Sidecar.logRequest(request)
try {
const updatedEntity = await SettlementService.update(request.params.name, request.payload)
if (request.payload.isActive !== undefined) {
const isActiveText = request.payload.isActive ? Enum.isActiveText.activated : Enum.isActiveText.disabled
const changeLog = JSON.stringify(Object.assign({}, request.params, { isActive: request.payload.isActive }))
Logger.info(`Settlement Model has been ${isActiveText} :: ${changeLog}`)
}
const ledgerAccountTypes = await Cache.getEnums('ledgerAccountType')
const ledgerAccountIds = Util.transpose(ledgerAccountTypes)
const settlementGranularityIds = Util.transpose(Enum.SettlementGranularity)
const settlementInterchangeIds = Util.transpose(Enum.SettlementInterchange)
const settlementDelayIds = Util.transpose(Enum.SettlementDelay)
return entityItem(updatedEntity, ledgerAccountIds, settlementGranularityIds, settlementInterchangeIds, settlementDelayIds)
} catch (err) {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}
const create = async function (request, h) {
Sidecar.logRequest(request)
try {
Expand All @@ -54,5 +118,8 @@ const create = async function (request, h) {
}

module.exports = {
create
create,
getByName,
getAll,
update
}
41 changes: 41 additions & 0 deletions src/api/settlementModels/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,27 @@ const ledgerAccountList = require('../../../seeds/ledgerAccountType.js').ledgerA
const tags = ['api', 'settlement']

module.exports = [
{
method: 'GET',
path: '/settlementModels',
handler: Handler.getAll,
options: {
tags
}
},
{
method: 'GET',
path: '/settlementModels/{name}',
handler: Handler.getByName,
options: {
tags,
validate: {
params: Joi.object({
name: Joi.string().required().description('SettlementModel name')
})
}
}
},
{
method: 'POST',
path: '/settlementModels',
Expand All @@ -58,5 +79,25 @@ module.exports = [
})
}
}
},
{
method: 'PUT',
path: '/settlementModels/{name}',
handler: Handler.update,
options: {
tags,
payload: {
allow: ['application/json'],
failAction: 'error'
},
validate: {
payload: Joi.object({
isActive: Joi.boolean().required().description('settlementModel isActive boolean')
}),
params: Joi.object({
name: Joi.string().required().description('settlementModel name')
})
}
}
}
]
30 changes: 28 additions & 2 deletions src/domain/settlement/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ const getByName = async (name) => {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}

const getAll = async () => {
try {
return await SettlementModelModel.getAll()
} catch (err) {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}
const getLedgerAccountTypeName = async (name) => {
try {
return await LedgerAccountTypeModel.getLedgerAccountByName(name)
Expand All @@ -54,8 +60,28 @@ const getLedgerAccountTypeName = async (name) => {
}
}

const update = async (name, payload) => {
try {
const settlementModel = await SettlementModelModel.getByName(name)
settlementModeExists(settlementModel)
await SettlementModelModel.update(settlementModel, payload.isActive)
settlementModel.isActive = +payload.isActive
return settlementModel
} catch (err) {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}

const settlementModeExists = (settlementModel) => {
if (settlementModel) {
return settlementModel
}
throw ErrorHandler.Factory.createInternalServerFSPIOPError('Settlement Model does not exist')
}
module.exports = {
createSettlementModel,
getLedgerAccountTypeName,
getByName
getByName,
getAll,
update
}
14 changes: 14 additions & 0 deletions src/models/settlement/settlementModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,17 @@ exports.getByName = async (name) => {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}
exports.getAll = async () => {
try {
return await Db.settlementModel.find()
} catch (err) {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}
exports.update = async (settlementModel, isActive) => {
try {
return await Db.settlementModel.update({ settlementModelId: settlementModel.settlementModelId }, { isActive })
} catch (err) {
throw ErrorHandler.Factory.reformatFSPIOPError(err)
}
}
124 changes: 122 additions & 2 deletions test/unit/api/settlementModels/handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,32 @@

const Test = require('tapes')(require('tape'))
const Sinon = require('sinon')

const Logger = require('@mojaloop/central-services-logger')
const Handler = require('../../../../src/api/settlementModels/handler')
const Sidecar = require('../../../../src/lib/sidecar')
const SettlementService = require('../../../../src/domain/settlement')
const Cache = require('../../../../src/lib/cache')
const FSPIOPError = require('@mojaloop/central-services-error-handling').Factory.FSPIOPError

const createRequest = ({ payload, params, query }) => {
const sandbox = Sinon.createSandbox()
const requestPayload = payload || {}
const requestParams = params || {}
const requestQuery = query || {}
const enums = sandbox.stub()
enums.withArgs('ledgerAccountType').returns({ POSITION: 1, SETTLEMENT: 2, HUB_RECONCILIATION: 3, HUB_MULTILATERAL_SETTLEMENT: 4, HUB_FEE: 5 })
return {
payload: requestPayload,
params: requestParams,
query: requestQuery,
server: {
log: () => { },
methods: {
enums
}
}
}
}

Test('SettlementModel', settlementModelHandlerTest => {
let sandbox
Expand All @@ -42,20 +63,50 @@ Test('SettlementModel', settlementModelHandlerTest => {
isActive: 1,
createdDate: '2018-07-17T16:04:24.185Z'
}
const settlementModelService = [

{
settlementModelId: 106,
name: 'DEFERRED_NET',
isActive: 1,
settlementGranularityId: 1,
settlementInterchangeId: 1,
settlementDelayId: 2,
currencyId: null,
requireLiquidityCheck: 1,
ledgerAccountTypeId: 2,
autoPositionReset: 1
}
]
const settlementModel = [

{
settlementModelId: 106,
name: 'DEFERRED_NET',
isActive: true,
settlementGranularity: 'GROSS',
settlementInterchange: 'BILATERAL',
settlementDelay: 'DEFERRED',
currency: null,
requireLiquidityCheck: true,
ledgerAccountTypeId: 'SETTLEMENT',
autoPositionReset: true
}
]
settlementModelHandlerTest.beforeEach(test => {
sandbox = Sinon.createSandbox()
sandbox.stub(Sidecar)
sandbox.stub(Logger)
sandbox.stub(SettlementService)
sandbox.stub(Cache)
Cache.getEnums.returns(Promise.resolve({ POSITION: 1, SETTLEMENT: 2, HUB_RECONCILIATION: 3, HUB_MULTILATERAL_SETTLEMENT: 4, HUB_FEE: 5 }))
test.end()
})

settlementModelHandlerTest.afterEach(test => {
sandbox.restore()
test.end()
})

settlementModelHandlerTest.test('Handler Test', async handlerTest => {
handlerTest.test('create should create a new settlement model and return a 201', async function (test) {
const payload = {
Expand Down Expand Up @@ -129,6 +180,75 @@ Test('SettlementModel', settlementModelHandlerTest => {
}
})

handlerTest.test('getAll should return all the settlement models', async function (test) {
SettlementService.getLedgerAccountTypeName.returns(Promise.resolve(ledgerAccountType))
SettlementService.getAll.returns(Promise.resolve(settlementModelService))
const result = await Handler.getAll()
test.deepEqual(result, settlementModel, 'The results match')
test.end()
})

handlerTest.test('getByName should return the settlement model', async function (test) {
SettlementService.getByName.withArgs(settlementModel[0].name).returns(Promise.resolve(settlementModelService[0]))
const result = await Handler.getByName(createRequest({ params: { name: settlementModel[0].name } }))
test.deepEqual(result, settlementModel[0], 'The results match')
test.end()
})

handlerTest.test('getByName should throw error', async function (test) {
SettlementService.getByName.withArgs(settlementModel[0].name).returns(Promise.resolve(null))
try {
await Handler.getByName(createRequest({ params: { name: settlementModel[0].name } }))
} catch (e) {
test.ok(e instanceof Error)
test.equal(e.message, 'The requested resource could not be found.')
test.end()
}
})

handlerTest.test('update should update, return settlement model and utilize logger', async function (test) {
SettlementService.update.withArgs(settlementModel[0].name, { isActive: 1 }).returns(Promise.resolve(settlementModelService[0]))
try {
const result = await Handler.update(createRequest({
params: { name: settlementModel[0].name },
payload: { isActive: 1 }
}))
test.deepEqual(result, settlementModel[0], 'The results match')
test.ok(Logger.info.withArgs('SettlementModel has been activated :: {"name":"DEFERRED_NET","isActive":1}'))
test.end()
} catch (err) {
test.fail('Error thrown')
test.end()
}
})

handlerTest.test('update should update, return settlement model if settlement model when inactive and utilize', async function (test) {
SettlementService.update.withArgs(settlementModel[0].name, { isActive: 0 }).returns(Promise.resolve(settlementModelService[0]))
try {
const result = await Handler.update(createRequest({
params: { name: settlementModel[0].name },
payload: { isActive: 0 }
}))
test.deepEqual(result, settlementModel[0], 'The results match')
test.ok(Logger.info.withArgs('SettlementModel has been disabled :: {"name":"DEFERRED_NET","isActive":0}'))
test.end()
} catch (err) {
test.fail('Error thrown')
test.end()
}
})

handlerTest.test('update should throw error', async function (test) {
SettlementService.update.withArgs(settlementModel[0].name, { isActive: 1 }).throws(new Error('Test error'))
try {
await Handler.update(createRequest({ params: { name: settlementModelService[0].name }, payload: { isActive: 1 } }))
} catch (e) {
test.ok(e instanceof FSPIOPError)
test.equal(e.message, 'Test error')
test.end()
}
})

handlerTest.end()
})

Expand Down
Loading

0 comments on commit 6324ca6

Please sign in to comment.