From 9888b896df83556d1b48a43dca9bc767713a3ab3 Mon Sep 17 00:00:00 2001 From: James Bush Date: Thu, 27 Jun 2019 10:15:49 +0100 Subject: [PATCH] 1. add second test party 2. remove unused dependencies 3. change default DFSPID 4. add linting config 5. add logging 6. add more comments 7. other tweaks --- src/.eslintrc.json | 30 +++++++++++ src/backend.env | 3 ++ src/data.json | 9 ++++ src/package.json | 3 +- src/scheme-adapter.env | 2 +- src/server.js | 114 ++++++++++++++++++++++++++++++++--------- 6 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 src/.eslintrc.json diff --git a/src/.eslintrc.json b/src/.eslintrc.json new file mode 100644 index 0000000..a43f178 --- /dev/null +++ b/src/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 9 + }, + "rules": { + "indent": [ + "error", + 4, + { "SwitchCase": 1 } + ], + "linebreak-style": [ + 2, + "unix" + ], + "quotes": [ + 2, + "single" + ], + "semi": [ + 2, + "always" + ], + "no-console": "off" + } +} diff --git a/src/backend.env b/src/backend.env index 673877a..84b67ae 100644 --- a/src/backend.env +++ b/src/backend.env @@ -1,2 +1,5 @@ # Port on which the mock DFSP backend service will listen for requests from the SDK scheme adapter LISTEN_PORT=3000 + +# Endpoint at which the scheme adapter outbound service can be found +#OUTBOUND_ENDPOINT=http://scheme-adapter:4001 diff --git a/src/data.json b/src/data.json index ed0d55b..5b86e05 100644 --- a/src/data.json +++ b/src/data.json @@ -12,6 +12,15 @@ "lastName": "Doe", "dateOfBirth": "1980-01-01", "merchantClassificationCode": "1234" + }, + "987654321": { + "type": "CONSUMER", + "displayName": "Jane Doe", + "firstName": "Jane", + "middleName": "Someone", + "lastName": "Doe", + "dateOfBirth": "1982-02-02", + "merchantClassificationCode": "4321" } } } diff --git a/src/package.json b/src/package.json index 3dd36f0..50b9aac 100644 --- a/src/package.json +++ b/src/package.json @@ -12,6 +12,7 @@ "dependencies": { "base64url": "^3.0.1", "express": "^4.16.3", - "node-fetch": "^2.3.0" + "request": "^2.88.0", + "request-promise-native": "^1.0.7" } } diff --git a/src/scheme-adapter.env b/src/scheme-adapter.env index a68eadf..b63f539 100644 --- a/src/scheme-adapter.env +++ b/src/scheme-adapter.env @@ -30,7 +30,7 @@ LOG_INDENT=0 # The DFSPID of this simulator. The simulator will accept any requests routed to # FSPIOP-Destination: $SCHEME_NAME. Other requests will be rejected. SCHEME_NAME=default -DFSP_ID=mojaloop-sdk +DFSP_ID=mojaloopSdkMockBackend # REDIS CACHE CONNECTION CACHE_HOST=redis diff --git a/src/server.js b/src/server.js index ecd788e..5ee257f 100644 --- a/src/server.js +++ b/src/server.js @@ -12,49 +12,74 @@ const util = require('util'); const express = require('express'); -const fetch = require('node-fetch'); -const Crypto = require('crypto'); -const base64url = require('base64url'); +const request = require('request-promise-native'); const app = express(); -const port = process.env['LISTEN_PORT'] || 3000; +const listenPort = process.env['LISTEN_PORT'] || 3000; +const outboundEndpoint = process.env['OUTBOUND_ENDPOINT'] || 'http://scheme-adapter:4001'; -const { participants, parties } = require('./data.json'); +const { parties } = require('./data.json'); let homeTransactionId = 1000000; + +/** + * Look for JSON bodies on all incoming requests + */ app.use(express.json({ type: '*/*'})); -//log all requests + +/** + * Log all requests + */ app.use((req, res, next) => { - console.log(`${Date.now()} ${req.method} ${req.originalUrl}`); - console.log(`${util.inspect(req.headers)}`); + console.log(`Request received: ${Date.now()} ${req.method} ${req.originalUrl}`); + console.log(`Request headers: ${util.inspect(req.headers)}`); if(req.body) { - console.log(`${util.inspect(req.body, { depth: 10 })}`); + console.log(`Request body: ${util.inspect(req.body, { depth: 10 })}`); } return next(); }); -//health check endpoint +/** + * Health check endpoint e.g. for Kubernetes + */ app.get('/', (req, res) => { //return 200 res.status(200).end(); }); +/** + * Handle get parties request. This method is called by the SDK to perform + * party lookups in the backend. In this mock we have a static set of party + * data loaded from a local JSON file. + */ app.get('/parties/:idType/:idValue', async (req, res) => { + console.log(`Party lookup received for ${req.params.idType} ${req.params.idValue}`); + const party = parties[req.params.idType][req.params.idValue]; if(party) { + console.log(`Returning party: ${util.inspect(party)}`); + return res.send(party); } + console.log('Party not found'); res.status(404).end(); }); +/** + * Handle post quote request. This method is called by the SDK to perform + * a quote request. This gives our backend an opportunity to charge fees + * for accepting a particular transfer. + */ app.post('/quoterequests', async (req, res) => { // always return zero fees + console.log(`Quote request received: ${util.inspect(req.body)}`); + res.send({ quoteId: req.body.quoteId, transactionId: req.body.transactionId, @@ -67,45 +92,88 @@ app.post('/quoterequests', async (req, res) => { }); +/** + * Handle post transfers request. This method is called by the SDK to inform the + * backend of an incoming money transfer. This is called when a transfer has been + * successfully received by the SDK. + */ app.post('/transfers', async (req, res) => { - // just increment homeTransactionId + // just increment homeTransactionId to simulate a backend creating a + // transaction to put the incoming funds in the payee acount. + console.log(`Incoming transfer received: ${util.inspect(req.body)}`); + res.send({ homeTransactionId: `${homeTransactionId++}` }); }); +/** + * Handle post send request. This method allows us to simulate outgoing transfers + * from a DFSP backend. + */ app.post('/send', async (req, res) => { + console.log(`Request to send outgoing transfer: ${util.inspect(req.body)}`); + + const reqOpts = { + method: 'POST', + uri: `${outboundEndpoint}/transfers`, + headers: buildHeaders(), + body: req.body, + json: true + }; + try { + console.log(`Executing HTTP POST: ${util.inspect(reqOpts)}`); + const result = await request(reqOpts); + res.send(result); + } + catch(err) { + console.log(`Error: ${err.stack || util.inspect(err)}`); + res.send({ + message: err.message || 'An error occured' + }); + res.status(500).end(); + } }); -// default 404 for unhandled routes +/** + * Return 404 for non handled routes + */ app.use((req, res) => { + console.log(`Path not supported: ${req.originalUrl}`); res.status(404).end(); }); -//start the server -const server = app.listen(port, () => { - console.log(`Listening on port ${port}`); +/** + * Start the server + */ +const server = app.listen(listenPort, () => { + console.log(`Listening on port ${listenPort}`); }); -const buildHeaders = (resourceType, source, dest) => { +/** + * Utility method to build a set of headers required by the SDK outbound API + * + * @returns {object} - Object containing key/value pairs of HTTP headers + */ +const buildHeaders = () => { let headers = { - 'Content-Type': `application/json`, - 'Accept': `application/json`, - 'Date': new Date().toUTCString(), - 'User-Agent': 'Mojaloop SDK' //node-fetch INSISTS on sending a user-agent header!? infuriating! + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Date': new Date().toUTCString() }; return headers; -} +}; -//shutdown gracefully on SIGTERM +/** + * Shutdown gracefully on SIGTERM + */ process.on('SIGTERM', () => { server.close(); }); -