Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfixes for POST-GET /transfer operations #96

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"expiresIn": 180000,
"generateTimeout": 30000
},
"ENDPOINT_SECURITY":{
"TLS": {
"rejectUnauthorized": true
}
},
"AMOUNT": {
"PRECISION": 10,
"SCALE": 2
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mojaloop/ml-api-adapter",
"version": "5.3.0",
"version": "5.4.0",
"description": "Convert from ML API to/from internal Central Services messaging format.",
"license": "Apache-2.0",
"private": true,
Expand Down
45 changes: 41 additions & 4 deletions src/domain/transfer/transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

'use strict'

const ENUM = require('../../lib/enum')

/**
* @module src/domain/transfer/transformer
*/
Expand All @@ -34,19 +36,29 @@
* @function transformHeaders
*
* @description This will transform the headers before sending to kafka
* NOTE: Assumes incoming headers keys are lowercased. This is a safe
* assumption only if the headers parameter comes from node default http framework.
*
* see https://nodejs.org/dist/latest-v10.x/docs/api/http.html#http_message_headers
*
* @param {object} headers - the http header from the request
*
* @returns {object} Returns the normalized headers
*/

const transformHeaders = (headers) => {
const transformHeaders = (headers, config) => {
// Normalized keys
var normalizedKeys = Object.keys(headers).reduce(
function (keys, k) {
keys[k.toLowerCase()] = k
return keys
}, {})
// Normalized headers
var normalizedHeaders = {}
for (var headerKey in headers) {
var headerValue = headers[headerKey]
switch (headerKey.toLowerCase()) {
case ('date'):
case (ENUM.headers.GENERAL.DATE):
var tempDate = {}
if (typeof headerValue === 'object' && headerValue instanceof Date) {
tempDate = headerValue.toUTCString()
Expand All @@ -62,15 +74,40 @@ const transformHeaders = (headers) => {
}
normalizedHeaders[headerKey] = tempDate
break
case ('content-length'):
case (ENUM.headers.GENERAL.CONTENT_LENGTH):
// Do nothing here, do not map. This will be inserted correctly by the Hapi framework.
break
case (ENUM.headers.FSPIOP.URI):
// Do nothing here, do not map. This will be removed from the callback request.
break
case (ENUM.headers.FSPIOP.HTTP_METHOD):
if (config.httpMethod.toLowerCase() === headerValue.toLowerCase()) {
// HTTP Methods match, and thus no change is required
normalizedHeaders[headerKey] = headerValue
} else {
// HTTP Methods DO NOT match, and thus a change is required for target HTTP Method
normalizedHeaders[headerKey] = config.httpMethod
}
break
case (ENUM.headers.FSPIOP.SIGNATURE):
// Check to see if we find a regex match the source header containing the switch name.
// If so we include the signature otherwise we remove it.

if (headers[normalizedKeys[ENUM.headers.FSPIOP.SOURCE]].match(ENUM.headers.FSPIOP.SWITCH.regex) === null) {
normalizedHeaders[headerKey] = headerValue
}
break
case (ENUM.headers.FSPIOP.SOURCE):
normalizedHeaders[headerKey] = config.sourceFsp
break
case (ENUM.headers.FSPIOP.DESTINATION):
normalizedHeaders[headerKey] = config.destinationFsp
break
default:
normalizedHeaders[headerKey] = headerValue
}
}
return normalizedHeaders
// return headers
}

module.exports = {
Expand Down
18 changes: 10 additions & 8 deletions src/handlers/notification/callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@

const Logger = require('@mojaloop/central-services-shared').Logger
const request = require('request')
// const request = require('request-promise-native') // added to ensure that the request support async-await or promise natively
const Transformer = require('../../domain/transfer/transformer')
const Config = require('../../lib/config')

/**
* @module src/handlers/notification/callbacks
Expand All @@ -45,32 +47,32 @@ const Transformer = require('../../domain/transfer/transformer')

* @returns {Promise} Returns a promise which resolves the http status code on success or rejects the error on failure
*/
const sendCallback = async (url, method, headers, message, cid, fsp) => {
const sendCallback = async (url, method, headers, message, cid, sourceFsp, destinationFsp) => {
// Transform headers into Mojaloop v1.0 Specifications
const transformedHeaders = Transformer.transformHeaders(headers)
const transformedHeaders = Transformer.transformHeaders(headers, { httpMethod: method, sourceFsp: sourceFsp, destinationFsp })

const requestOptions = {
url,
method,
headers: transformedHeaders,
body: JSON.stringify(message),
agentOptions: {
rejectUnauthorized: false
rejectUnauthorized: Config.ENDPOINT_SECURITY_TLS.rejectUnauthorized
}
}

Logger.info(`[cid=${cid}, fsp=${fsp}] ~ NotificationHandler::sendCallback := Callback URL: ${url}`)
Logger.debug(`[cid=${cid}, fsp=${fsp}] ~ NotificationHandler::sendCallback := Callback requestOptions: ${JSON.stringify(requestOptions)}`)
Logger.info(`[cid=${cid}, sourceFsp=${sourceFsp}, destinationFsp=${destinationFsp}] ~ NotificationHandler::sendCallback := Callback URL: ${url}`)
Logger.debug(`[cid=${cid}, sourceFsp=${sourceFsp}, destinationFsp=${destinationFsp}] ~ NotificationHandler::sendCallback := Callback requestOptions: ${JSON.stringify(requestOptions)}`)

return new Promise((resolve, reject) => {
return request(requestOptions, (error, response, body) => {
if (error) {
// throw error // this is not correct in the context of a Promise.
Logger.error(`[cid=${cid}, fsp=${fsp}] ~ NotificationHandler::sendCallback := Callback failed with error: ${error}, response: ${JSON.stringify(response)}`)
Logger.error(`[cid=${cid}, sourceFsp=${sourceFsp}, destinationFsp=${destinationFsp}] ~ NotificationHandler::sendCallback := Callback failed with error: ${error}, response: ${JSON.stringify(response)}`)
return reject(error)
}
Logger.info(`[cid=${cid}, fsp=${fsp}] ~ NotificationHandler::sendCallback := Callback successful with status code: ${response.statusCode}`)
Logger.debug(`[cid=${cid}, fsp=${fsp}] ~ NotificationHandler::sendCallback := Callback successful with response: ${JSON.stringify(response)}`)
Logger.info(`[cid=${cid}, sourceFsp=${sourceFsp}, destinationFsp=${destinationFsp}] ~ NotificationHandler::sendCallback := Callback successful with status code: ${response.statusCode}`)
Logger.debug(`[cid=${cid}, sourceFsp=${sourceFsp}, destinationFsp=${destinationFsp}] ~ NotificationHandler::sendCallback := Callback successful with response: ${JSON.stringify(response)}`)
return resolve(response.statusCode)
})
})
Expand Down
Loading