Skip to content

Commit

Permalink
feat(mojaloop/#3933): update participant accounts validation (#333)
Browse files Browse the repository at this point in the history
feat(mojaloop/#3933): update participant accounts validation
- Added validation for `supportedCurrencies` when present in `POST /quotes` request (payer DFSP must have position account for all supported currencies
- Added validation for `payeeReceiveAmount` currency when present (payee DFSP must have position account for the `payeeReceiveAmount` currency)
- Fixed error handling to prevent sending double error callbacks
- Ensure top-level tracing spans are finished correctly
- Updated integration tests harness to support new changes
- Added integration tests for the use case
- Updated dependencies and resolve audit issues
  • Loading branch information
oderayi authored Jun 6, 2024
1 parent 8fcf844 commit 1b57edc
Show file tree
Hide file tree
Showing 13 changed files with 537 additions and 110 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ x-healthcheck-params: &healthcheckParams
services:
quoting-service:
<<: *quotingServiceBase
command: npm run start
ports:
- "3002:3002"
- "19229:9229"
Expand Down
91 changes: 57 additions & 34 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "quoting-service",
"description": "Quoting Service hosted by a scheme",
"license": "Apache-2.0",
"version": "15.8.0-snapshot.15",
"version": "15.8.0-snapshot.16",
"author": "ModusBox",
"contributors": [
"Georgi Georgiev <georgi.georgiev@modusbox.com>",
Expand Down Expand Up @@ -49,7 +49,9 @@
"scripts": {
"start": "npm run start:api",
"start:api": "node src/index.js",
"start:debug": "node --inspect=0.0.0.0 src/index.js",
"start:handlers": "node src/handlers/index.js h --quotes --bulk_quotes --fx_quotes",
"start:handlers:debug": "node --inspect=0.0.0.0 src/handlers/index.js h --quotes --bulk_quotes --fx_quotes",
"watch:api": "npx nodemon src/index.js",
"dev": "npm run docker:stop && docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build -d",
"lint": "npx standard",
Expand All @@ -59,7 +61,7 @@
"test:coverage": "jest --coverage --coverageThreshold='{}' --testMatch '**/test/unit/**/*.test.js'",
"test:coverage-check": "jest --runInBand --forceExit --coverage --testMatch '**/test/unit/**/*.test.js'",
"test:junit": "jest --runInBand --forceExit --reporters=default --reporters=jest-junit --testMatch '**/test/unit/**/*.test.js'",
"test:int": "jest --testMatch '**/test/integration/**/*.test.js'",
"test:int": "jest --runInBand --testMatch '**/test/integration/**/*.test.js'",
"regenerate": "yo swaggerize:test --framework hapi --apiPath './src/interface/swagger.json'",
"package-lock": "docker run --rm -it quoting-service:local cat package-lock.json > package-lock.json",
"run": "docker run -p 3002:3002 --rm --link db:mysql quoting-service:local",
Expand Down Expand Up @@ -102,7 +104,7 @@
"@mojaloop/event-sdk": "14.1.0",
"@mojaloop/ml-number": "11.2.4",
"@mojaloop/sdk-standard-components": "18.1.0",
"ajv": "8.14.0",
"ajv": "8.16.0",
"ajv-keywords": "5.1.0",
"axios": "1.7.2",
"blipp": "4.0.2",
Expand All @@ -122,7 +124,7 @@
"rc": "1.2.8"
},
"devDependencies": {
"audit-ci": "^6.6.1",
"audit-ci": "^7.0.1",
"eslint": "8.16.0",
"eslint-config-standard": "17.1.0",
"eslint-plugin-jest": "28.5.0",
Expand Down
36 changes: 36 additions & 0 deletions src/handlers/QuotingHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class QuotingHandler {
const fspiopError = reformatFSPIOPError(err)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, payload.quoteId, fspiopError, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -99,6 +103,10 @@ class QuotingHandler {
this.logger.error(`error in handlePutQuotes partition:${requestData.partition}, offset:${requestData.offset}: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, quoteId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -117,6 +125,10 @@ class QuotingHandler {
this.logger.error(`error in handleGetQuotes partition:${requestData.partition}, offset:${requestData.offset}: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, quoteId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -136,6 +148,10 @@ class QuotingHandler {
const fspiopError = reformatFSPIOPError(err)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, payload.bulkQuoteId, fspiopError, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -157,6 +173,10 @@ class QuotingHandler {
this.logger.error(`error in handlePutBulkQuotes partition:${requestData.partition}, offset:${requestData.offset}: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, bulkQuoteId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -175,6 +195,10 @@ class QuotingHandler {
this.logger.error(`error in handleGetBulkQuotes partition:${requestData.partition}, offset:${requestData.offset}: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, bulkQuoteId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -194,6 +218,10 @@ class QuotingHandler {
const fspiopError = reformatFSPIOPError(err)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, payload.conversionRequestId, fspiopError, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -215,6 +243,10 @@ class QuotingHandler {
this.logger.error(`error in handlePutFxQuotes: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, conversionRequestId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand All @@ -233,6 +265,10 @@ class QuotingHandler {
this.logger.error(`error in handleGetBulkQuotes: ${err?.stack}`)
const fspiopSource = headers[FSPIOP.SOURCE]
await model.handleException(fspiopSource, conversionRequestId, err, headers, span)
} finally {
if (span && !span.isFinished) {
span.finish()
}
}

return true
Expand Down
8 changes: 4 additions & 4 deletions src/lib/enum.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@
'use strict'

const PAYER = 'PAYER'

const PAYEE = 'PAYEE'

const PAYER_DFSP = 'PAYER_DFSP'

const PAYEE_DFSP = 'PAYEE_DFSP'

const INITIATING_FSP = 'INITIATING_FSP'
const COUNTERPARTY_FSP = 'COUNTERPARTY_FSP'
const PRINCIPLE_VALUE = 'PRINCIPLE_VALUE'

const Functionalities = Object.freeze({
Expand All @@ -57,6 +55,8 @@ module.exports = {
PAYER,
PAYER_DFSP,
PAYEE_DFSP,
INITIATING_FSP,
COUNTERPARTY_FSP,
PRINCIPLE_VALUE,
Functionalities,
ErrorMessages
Expand Down
17 changes: 9 additions & 8 deletions src/model/fxQuotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ class FxQuotesModel {
*
* @returns {promise} - promise will reject if request is not valid
*/
async validateFxQuoteRequest (fspiopSource, fspiopDestination, fxQuoteRequest) {
await this.db.getParticipant(fspiopSource, LOCAL_ENUM.PAYER_DFSP, fxQuoteRequest.conversionTerms.sourceAmount.currency, ENUM.Accounts.LedgerAccountType.POSITION)
await this.db.getParticipant(fspiopDestination, LOCAL_ENUM.PAYEE_DFSP, fxQuoteRequest.conversionTerms.sourceAmount.currency, ENUM.Accounts.LedgerAccountType.POSITION)
async validateFxQuoteRequest (fspiopDestination, fxQuoteRequest) {
const currencies = [fxQuoteRequest.conversionTerms.sourceAmount.currency, fxQuoteRequest.conversionTerms.targetAmount.currency]
await Promise.all(currencies.map(async (currency) => {
await this.db.getParticipant(fspiopDestination, LOCAL_ENUM.COUNTERPARTY_FSP, currency, ENUM.Accounts.LedgerAccountType.POSITION)
}))
}

/**
Expand All @@ -58,15 +60,14 @@ class FxQuotesModel {
*/
async handleFxQuoteRequest (headers, fxQuoteRequest, span) {
let fspiopSource
let childSpan
const childSpan = span.getChild('qs_fxquote_forwardFxQuoteRequest')
try {
await childSpan.audit({ headers, payload: fxQuoteRequest }, EventSdk.AuditEventAction.start)

fspiopSource = headers[ENUM.Http.Headers.FSPIOP.SOURCE]
const fspiopDestination = headers[ENUM.Http.Headers.FSPIOP.DESTINATION]

await this.validateFxQuoteRequest(fspiopSource, fspiopDestination, fxQuoteRequest)

childSpan = span.getChild('qs_fxquote_forwardFxQuoteRequest')
await childSpan.audit({ headers, payload: fxQuoteRequest }, EventSdk.AuditEventAction.start)
await this.validateFxQuoteRequest(fspiopDestination, fxQuoteRequest)

await this.forwardFxQuoteRequest(headers, fxQuoteRequest.conversionRequestId, fxQuoteRequest, childSpan)
} catch (err) {
Expand Down
Loading

0 comments on commit 1b57edc

Please sign in to comment.