Skip to content

Commit

Permalink
fix(mojaloop/#2538): fspiop api version negotiation not handled
Browse files Browse the repository at this point in the history
- fix(mojaloop/#2538): fspiop api version negotiation not handled - mojaloop/project#2538
    - added getOptionsForFSPIOPHeaderValidation to the plugin configuration
    - added new config key for PROTOCOL_VERSIONS to default and integration configs
    - added DEFAULT_PROTOCOL_VERSION for backward compatibility
    - updated config and unit tests to FSPIOP v1.1 protocol version
    - BKAPI_PROTOCOL_VERSIONS__ACCEPT__VALIDATELIST can be set as follows "[ \"1\", \"1.1\"]" and it will be parsed correctly into a object
    - Added unit tests for config changes

BREAKING CHANGE: Forcing a major version change for awareness of the config changes. The `LIB_RESOURCE_VERSIONS` env var is now deprecated, and this is now also controlled by the PROTOCOL_VERSIONS config in the default.json. This has been done for consistency between all API services going forward and unifies the config for both inbound and outbound Protocol API validation/transformation features.
  • Loading branch information
mdebarros committed Nov 17, 2021
1 parent 2255f22 commit 41bb7d5
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 2 deletions.
10 changes: 10 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{
"PORT": 3003,
"HOSTNAME": "http://bulk-api-adapter",
"PROTOCOL_VERSIONS": {
"CONTENT": "1.1",
"ACCEPT": {
"DEFAULT": "1",
"VALIDATELIST": [
"1",
"1.1"
]
}
},
"ENDPOINT_SOURCE_URL": "http://localhost:3001/participants/{{fsp}}/endpoints",
"ENDPOINT_HEALTH_URL": "http://localhost:3001/health",
"ENDPOINT_CACHE_CONFIG": {
Expand Down
23 changes: 22 additions & 1 deletion src/lib/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
const RC = require('parse-strings-in-object')(require('rc')('BKAPI', require('../../config/default.json')))

const DEFAULT_PROTOCOL_VERSION = {
CONTENT: '1.1',
ACCEPT: {
DEFAULT: '1', // This is not currently used by this service, but it is here for consistency between services. In future if we need to default the ACCEPT protocol, then this should be used.
VALIDATELIST: [
'1',
'1.1'
]
}
}

const getProtocolVersions = (defaultProtocolVersions, overrideProtocolVersions) => {
const T_PROTOCOL_VERSION = { ...defaultProtocolVersions, ...overrideProtocolVersions }
if (overrideProtocolVersions && overrideProtocolVersions.ACCEPT) T_PROTOCOL_VERSION.ACCEPT = { ...defaultProtocolVersions.ACCEPT, ...overrideProtocolVersions.ACCEPT }
if (T_PROTOCOL_VERSION.ACCEPT && T_PROTOCOL_VERSION.ACCEPT.VALIDATELIST && (typeof T_PROTOCOL_VERSION.ACCEPT.VALIDATELIST === 'string' || T_PROTOCOL_VERSION.ACCEPT.VALIDATELIST instanceof String)) {
T_PROTOCOL_VERSION.ACCEPT.VALIDATELIST = JSON.parse(T_PROTOCOL_VERSION.ACCEPT.VALIDATELIST)
}
return T_PROTOCOL_VERSION
}

// Set config object to be returned
const config = {
HOSTNAME: RC.HOSTNAME.replace(/\/$/, ''),
Expand All @@ -19,7 +39,8 @@ const config = {
INSTRUMENTATION_METRICS_CONFIG: RC.INSTRUMENTATION.METRICS.config,
ENDPOINT_SECURITY: RC.ENDPOINT_SECURITY,
ENDPOINT_SECURITY_TLS: RC.ENDPOINT_SECURITY.TLS,
MAX_FULFIL_TIMEOUT_DURATION_SECONDS: RC.MAX_FULFIL_TIMEOUT_DURATION_SECONDS
MAX_FULFIL_TIMEOUT_DURATION_SECONDS: RC.MAX_FULFIL_TIMEOUT_DURATION_SECONDS,
PROTOCOL_VERSIONS: getProtocolVersions(DEFAULT_PROTOCOL_VERSION, RC.PROTOCOL_VERSIONS)
}

module.exports = config
29 changes: 28 additions & 1 deletion src/shared/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,41 @@ const registerPlugins = async (server) => {
plugin: require('hapi-auth-bearer-token')
})

// Helper to construct FSPIOPHeaderValidation option configuration
const getOptionsForFSPIOPHeaderValidation = () => {
// configure supported FSPIOP Content-Type versions
const supportedProtocolContentVersions = [Config.PROTOCOL_VERSIONS.CONTENT.toString()]

// configure supported FSPIOP Accept version
const supportedProtocolAcceptVersions = []
for (const version of Config.PROTOCOL_VERSIONS.ACCEPT.VALIDATELIST) {
supportedProtocolAcceptVersions.push(version.toString())
}

// configure FSPIOP resources
const resources = [
'transfers'
]

// return FSPIOPHeaderValidation plugin options
return {
resources,
supportedProtocolContentVersions,
supportedProtocolAcceptVersions
}
}

await server.register([
Inert,
Vision,
Blipp,
ErrorHandling,
CentralServices.Util.Hapi.HapiRawPayload,
CentralServices.Util.Hapi.HapiEventPlugin,
CentralServices.Util.Hapi.FSPIOPHeaderValidation
{
plugin: CentralServices.Util.Hapi.FSPIOPHeaderValidation.plugin,
options: getOptionsForFSPIOPHeaderValidation()
}
])
}

Expand Down
85 changes: 85 additions & 0 deletions test/unit/lib/config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*****
License
--------------
Copyright © 2017 Bill & Melinda Gates Foundation
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
http://www.apache.org/licenses/LICENSE-2.0
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.
Contributors
--------------
This is the official list of the Mojaloop project contributors for this file.
Names of the original copyright holders (individuals or organizations)
should be listed with a '*' in the first column. People who have
contributed from an organization can be listed under the organization
that actually holds the copyright for their contributions (see the
Gates Foundation organization for an example). Those individuals should have
their names indented and be marked with a '-'. Email address can be added
optionally within square brackets <email>.
* Gates Foundation
- Name Surname <name.surname@gatesfoundation.com>
* Shashikant Hirugade <shashikant.hirugade@modusbox.com>
* Miguel de Barros <miguel.debarros@modusbox.com>
--------------
******/
'use strict'

const src = '../../../src/'
const Test = require('tapes')(require('tape'))
const Util = require('@mojaloop/central-services-shared').Util
const Default = require('../../../config/default.json')
const Proxyquire = require('proxyquire')

Test('Config tests', configTest => {
let sandbox
const Sinon = require('sinon')

configTest.beforeEach(t => {
sandbox = Sinon.createSandbox()
t.end()
})

configTest.afterEach(t => {
sandbox.restore()
t.end()
})

configTest.test('getFileContent should', async getFileContentTest => {
getFileContentTest.test('should not throw', test => {
try {
const DefaultStub = Util.clone(Default)
const Config = Proxyquire(`${src}/lib/config`, {
'../../config/default.json': DefaultStub
})
test.ok(Config)
test.ok('pass')
} catch (e) {
test.fail('should throw')
}
test.end()
})

getFileContentTest.test('should pass ENV var BKAPI_PROTOCOL_VERSIONS__ACCEPT__VALIDATELIST as a string', test => {
try {
const DefaultStub = Util.clone(Default)
// set env var
const validateList = ['1']
process.env.BKAPI_PROTOCOL_VERSIONS__ACCEPT__VALIDATELIST = JSON.stringify(validateList)
const Config = Proxyquire(`${src}/lib/config`, {
'../../config/default.json': DefaultStub
})
test.ok(Config)
test.ok('pass')
test.deepEqual(Config.PROTOCOL_VERSIONS.ACCEPT.VALIDATELIST, validateList)
} catch (e) {
test.fail('should throw')
}
test.end()
})

getFileContentTest.end()
})

configTest.end()
})

0 comments on commit 41bb7d5

Please sign in to comment.