From 1959d48100e8c899a7af89aebe64e364abf08636 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Thu, 25 Oct 2018 10:06:42 -0400 Subject: [PATCH 01/20] playing with matching template --- lib/util/index.js | 44 +++++++++++++++++++++++++++++++++++++++++++- routes/virtual.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/util/index.js b/lib/util/index.js index 6a53c81c..d1d6e0d1 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -22,6 +22,48 @@ global.deepEquals = function(a, b) { } }; +global.flattenObject = (function (isArray, wrapped) { + return function (table) { + return reduce("", {}, table); + }; + + function reduce(path, accumulator, table) { + if (isArray(table)) { + var length = table.length; + + if (length) { + var index = 0; + + while (index < length) { + var property = path + "[" + index + "]", item = table[index++]; + if (wrapped(item) !== item) accumulator[property] = item; + else reduce(property, accumulator, item); + } + } else accumulator[path] = table; + } else { + var empty = true; + + if (path) { + for (var property in table) { + var item = table[property], property = path + "." + property, empty = false; + if (wrapped(item) !== item) accumulator[property] = item; + else reduce(property, accumulator, item); + } + } else { + for (var property in table) { + var item = table[property], empty = false; + if (wrapped(item) !== item) accumulator[property] = item; + else reduce(property, accumulator, item); + } + } + + if (empty) accumulator[path] = table; + } + + return accumulator; + } +}(Array.isArray, Object)); + // function to read all files in directory global.readFiles = function(dirname, onFileContent, onError) { fs.readdir(dirname, function(err, filenames) { @@ -44,7 +86,7 @@ global.readFiles = function(dirname, onFileContent, onError) { // polyfill for Object.entries() if (!Object.entries) Object.entries = function(obj) { - var ownProps = Object.keys(obj), + let ownProps = Object.keys(obj), i = ownProps.length, resArray = new Array(i); // preallocate the Array while (i--) diff --git a/routes/virtual.js b/routes/virtual.js index 76f15361..8e8dd3c9 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -1,6 +1,7 @@ const express = require('express'); const router = express.Router(); const xml2js = require('xml2js'); +const Builder = new xml2js.Builder(); const pause = require('connect-pause'); const debug = require('debug')('matching'); const Service = require('../models/Service'); @@ -61,6 +62,35 @@ function registerRRPair(service, rrpair) { } } + // begin playing with matching template + + const template = {}; + + debug(Builder.buildObject(template)); + + const trimmedPayload = {}; + const trimmedReqData = {}; + + const flatTemplate = flattenObject(template); + const flatPayload = flattenObject(payload); + const flatReqData = flattenObject(reqData); + + debug(JSON.stringify(flatPayload, null, 2)); + debug(JSON.stringify(flatReqData, null, 2)); + + for (let field in flatTemplate) { + trimmedPayload[field] = flatPayload[field]; + trimmedReqData[field] = flatReqData[field]; + } + + debug(JSON.stringify(trimmedPayload, null, 2)); + debug(JSON.stringify(trimmedReqData, null, 2)); + + debug(deepEquals(trimmedPayload, trimmedReqData)); + return; + + // end playing with matching template + if (!rrpair.reqData || deepEquals(payload, reqData)) { // check request queries if (rrpair.queries) { From 0589860c4208ae703df8141d153547e9d6657890 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Fri, 26 Oct 2018 08:53:28 -0400 Subject: [PATCH 02/20] progress with matching template --- models/Service.js | 1 + routes/virtual.js | 55 ++++++++++++++++++++++++++--------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/models/Service.js b/models/Service.js index 5aaab9fb..eeaf3fbb 100644 --- a/models/Service.js +++ b/models/Service.js @@ -16,6 +16,7 @@ const serviceSchema = new mongoose.Schema({ type: String, index: true }, + matchTemplate: mongoose.Schema.Types.Mixed, rrpairs: [RRPair.schema], delay: { // force integer only diff --git a/routes/virtual.js b/routes/virtual.js index 8e8dd3c9..48461216 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -63,35 +63,40 @@ function registerRRPair(service, rrpair) { } // begin playing with matching template - - const template = {}; + let match = false; + let template = service.matchTemplate; - debug(Builder.buildObject(template)); - - const trimmedPayload = {}; - const trimmedReqData = {}; - - const flatTemplate = flattenObject(template); - const flatPayload = flattenObject(payload); - const flatReqData = flattenObject(reqData); - - debug(JSON.stringify(flatPayload, null, 2)); - debug(JSON.stringify(flatReqData, null, 2)); - - for (let field in flatTemplate) { - trimmedPayload[field] = flatPayload[field]; - trimmedReqData[field] = flatReqData[field]; + if (template) { + if (rrpair.payloadType === 'XML') { + template = Builder.buildObject(template); + } + + const trimmedPayload = {}; + const trimmedReqData = {}; + + const flatTemplate = flattenObject(template); + const flatPayload = flattenObject(payload); + const flatReqData = flattenObject(reqData); + + // debug(JSON.stringify(flatPayload, null, 2)); + // debug(JSON.stringify(flatReqData, null, 2)); + + for (let field in flatTemplate) { + trimmedPayload[field] = flatPayload[field]; + trimmedReqData[field] = flatReqData[field]; + } + + // debug(JSON.stringify(trimmedPayload, null, 2)); + // debug(JSON.stringify(trimmedReqData, null, 2)); + + match = deepEquals(trimmedPayload, trimmedReqData); + } + else { + match = deepEquals(payload, reqData); } - - debug(JSON.stringify(trimmedPayload, null, 2)); - debug(JSON.stringify(trimmedReqData, null, 2)); - - debug(deepEquals(trimmedPayload, trimmedReqData)); - return; - // end playing with matching template - if (!rrpair.reqData || deepEquals(payload, reqData)) { + if (!rrpair.reqData || match) { // check request queries if (rrpair.queries) { // try the next rr pair if no queries were sent From e19d10885b4b88581cde6fc5415f8881b26dcde5 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Mon, 29 Oct 2018 11:40:17 -0400 Subject: [PATCH 03/20] progress on matching template --- routes/virtual.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/routes/virtual.js b/routes/virtual.js index 48461216..f16acbbd 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -1,7 +1,6 @@ const express = require('express'); const router = express.Router(); const xml2js = require('xml2js'); -const Builder = new xml2js.Builder(); const pause = require('connect-pause'); const debug = require('debug')('matching'); const Service = require('../models/Service'); @@ -68,7 +67,9 @@ function registerRRPair(service, rrpair) { if (template) { if (rrpair.payloadType === 'XML') { - template = Builder.buildObject(template); + xml2js.parseString(template, function(err, xmlTemplate) { + template = xmlTemplate; + }); } const trimmedPayload = {}; @@ -86,8 +87,8 @@ function registerRRPair(service, rrpair) { trimmedReqData[field] = flatReqData[field]; } - // debug(JSON.stringify(trimmedPayload, null, 2)); - // debug(JSON.stringify(trimmedReqData, null, 2)); + debug(JSON.stringify(trimmedPayload, null, 2)); + debug(JSON.stringify(trimmedReqData, null, 2)); match = deepEquals(trimmedPayload, trimmedReqData); } From f609cd9ef31cafbd4d295f852ee417aae50196fe Mon Sep 17 00:00:00 2001 From: HymaSree Date: Mon, 29 Oct 2018 21:15:59 +0530 Subject: [PATCH 04/20] Fix for Issue50 - Standard Error Msg and Issue78 - Check/uncheck Create new Group --- app.js | 7 +++++++ public/partials/addapiform.html | 4 ++-- public/partials/spec.html | 4 ++-- routes/virtual.js | 10 ++++++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 59de9655..0beccc0a 100644 --- a/app.js +++ b/app.js @@ -149,6 +149,13 @@ function init() { const users = require('./routes/users'); app.use('/api/users', users); + //Error Handling + app.use(function(err, req, res, next){ + console.log(err.stack); + res.status(404).send(err.message); + }); + + // ready for testing (see test/test.js) app.emit('started'); } diff --git a/public/partials/addapiform.html b/public/partials/addapiform.html index 2a2eca65..d71dd3d3 100644 --- a/public/partials/addapiform.html +++ b/public/partials/addapiform.html @@ -20,14 +20,14 @@

Mock a REST or SOAP Service

-

Check to create new Group -   +  

diff --git a/public/partials/spec.html b/public/partials/spec.html index fa6dcbed..027a93d1 100644 --- a/public/partials/spec.html +++ b/public/partials/spec.html @@ -20,14 +20,14 @@

Mock from WSDL or OpenAPI

-

Check to create new Group -   +  

diff --git a/routes/virtual.js b/routes/virtual.js index 76f15361..68ec83ca 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -38,12 +38,18 @@ function registerRRPair(service, rrpair) { } else { debug("HTTP methods don't match"); - return next(); + if(matched) + return next(); + else if( matched == false) + return next(new Error("HTTP methods does not match")); } debug("Request matched? " + matched); // run the next callback if request not matched - if (!matched) return next(); + if (!matched){ + debug("Error in request payload :: matched: " + matched); + return next(new Error("Error in Request payload")); + } // function for matching requests to responses function matchRequest(payload) { From cfe0cb78ec47dc331e7c765773aa6b7412e29f38 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Mon, 29 Oct 2018 13:12:09 -0400 Subject: [PATCH 05/20] clean up after unit tests --- controllers/systemController.js | 13 +++++++++- controllers/userController.js | 13 +++++++++- lib/util/index.js | 33 +++++++++++++++++++++++-- package.json | 4 ++- routes/services.js | 29 +--------------------- routes/systems.js | 1 + routes/users.js | 1 + routes/virtual.js | 16 +++++------- tests/test.js | 44 ++++++++++++++++++++++++--------- 9 files changed, 100 insertions(+), 54 deletions(-) diff --git a/controllers/systemController.js b/controllers/systemController.js index 4d2ce8b4..711b8842 100644 --- a/controllers/systemController.js +++ b/controllers/systemController.js @@ -36,7 +36,18 @@ function addSystem(req, res) { }); } +function delSystem(req, res) { + System.findOneAndRemove({ name: req.params.name }, function(err) { + if (err) { + handleError(err, res, 400); + return; + } + res.json({ 'message' : 'deleted system', 'name' : req.params.name }); + }); +} + module.exports = { getSystems: getSystems, - addSystem: addSystem + addSystem: addSystem, + delSystem: delSystem } \ No newline at end of file diff --git a/controllers/userController.js b/controllers/userController.js index c6dc8317..01bb396c 100644 --- a/controllers/userController.js +++ b/controllers/userController.js @@ -11,6 +11,17 @@ function getUsers(req, res) { }); } +function delUser(req, res) { + User.findOneAndRemove({ uid : req.params.name }, function(err) { + if (err) { + handleError(err, res, 400); + return; + } + res.json({ 'message' : 'deleted user', 'username' : req.params.name }); + }); +} + module.exports = { - getUsers: getUsers + getUsers: getUsers, + delUser: delUser } \ No newline at end of file diff --git a/lib/util/index.js b/lib/util/index.js index d1d6e0d1..186ab3f3 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,4 +1,5 @@ -const fs = require('fs'); +const fs = require('fs'); +const jwt = require('jsonwebtoken'); const assert = require('assert'); const debug = require('debug')('default'); @@ -81,7 +82,35 @@ global.readFiles = function(dirname, onFileContent, onError) { }); }); }); -} +}; + +global.tokenMiddleware = function(req, res, next) { + res.set('Content-Type', 'application/json'); + if (req.method === 'GET') return next(); + + const token = req.query.token || req.headers['x-access-token']; + if (token) { + // verify secret and check expiry + jwt.verify(token, require('../../app').get('secret'), function(err, decoded) { + if (err) { + return res.status(403).json({ + success: false, + message: 'Failed to authenticate token' + }); + } else { + // save to request for use in other routes + req.decoded = decoded; + next(); + } + }); + } + else { + return res.status(401).json({ + success: false, + message: 'No token provided.' + }); + } +}; // polyfill for Object.entries() if (!Object.entries) diff --git a/package.json b/package.json index 5d3ab356..0eab8ce2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ }, "nyc": { "exclude": [ - "tests" + "tests", + "lib/pm2", + "lib/remove-route" ] }, "dependencies": { diff --git a/routes/services.js b/routes/services.js index 4d70e88f..b2757bb5 100644 --- a/routes/services.js +++ b/routes/services.js @@ -4,37 +4,10 @@ const router = express.Router(); const multer = require('multer'); const upload = multer({ dest: 'uploads/' }); -const jwt = require('jsonwebtoken'); const servCtrl = require('../controllers/serviceController'); // middleware for token auth -router.use(function(req, res, next) { - res.set('Content-Type', 'application/json'); - if (req.method === 'GET') return next(); - - const token = req.query.token || req.headers['x-access-token']; - if (token) { - // verify secret and check expiry - jwt.verify(token, require('../app').get('secret'), function(err, decoded) { - if (err) { - return res.status(403).json({ - success: false, - message: 'Failed to authenticate token' - }); - } else { - // save to request for use in other routes - req.decoded = decoded; - next(); - } - }); - } - else { - return res.status(401).json({ - success: false, - message: 'No token provided.' - }); - } -}); +router.use(tokenMiddleware); // middleware to reject invalid services function rejectInvalid(req, res, next) { diff --git a/routes/systems.js b/routes/systems.js index 1928f96a..dd333df8 100644 --- a/routes/systems.js +++ b/routes/systems.js @@ -4,5 +4,6 @@ const router = express.Router(); router.get('/', sysCtrl.getSystems); router.post('/', sysCtrl.addSystem); +router.delete('/:name', tokenMiddleware, sysCtrl.delSystem); module.exports = router; diff --git a/routes/users.js b/routes/users.js index e4d49a6e..d84f78aa 100644 --- a/routes/users.js +++ b/routes/users.js @@ -3,5 +3,6 @@ const express = require('express'); const router = express.Router(); router.get('/', userCtrl.getUsers); +router.delete('/:name', tokenMiddleware, userCtrl.delUser); module.exports = router; diff --git a/routes/virtual.js b/routes/virtual.js index f16acbbd..230a3d47 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -61,7 +61,7 @@ function registerRRPair(service, rrpair) { } } - // begin playing with matching template + // match request body based on template let match = false; let template = service.matchTemplate; @@ -71,17 +71,13 @@ function registerRRPair(service, rrpair) { template = xmlTemplate; }); } - - const trimmedPayload = {}; - const trimmedReqData = {}; - + const flatTemplate = flattenObject(template); const flatPayload = flattenObject(payload); const flatReqData = flattenObject(reqData); - - // debug(JSON.stringify(flatPayload, null, 2)); - // debug(JSON.stringify(flatReqData, null, 2)); - + + const trimmedPayload = {}; const trimmedReqData = {}; + for (let field in flatTemplate) { trimmedPayload[field] = flatPayload[field]; trimmedReqData[field] = flatReqData[field]; @@ -92,10 +88,10 @@ function registerRRPair(service, rrpair) { match = deepEquals(trimmedPayload, trimmedReqData); } + // else match against all fields else { match = deepEquals(payload, reqData); } - // end playing with matching template if (!rrpair.reqData || match) { // check request queries diff --git a/tests/test.js b/tests/test.js index 955e91fb..649224bb 100644 --- a/tests/test.js +++ b/tests/test.js @@ -20,6 +20,10 @@ const mockUser = { password: getRandomString() } +const mockGroup = { + name: getRandomString() +}; + function getRandomString() { return Math.random().toString(36).substring(2, 15); } @@ -165,15 +169,16 @@ describe('API tests', function() { .end(done); }); }); - - describe('Retrieve users', function() { - it('Responds with the users', function(done) { + + describe('Create new group', function() { + it('Responds with the group', function(done) { request - .get('/api/users') + .post('/api/systems') + .send(mockGroup) .expect(200) .end(done); }); - }); + }); describe('Retrieve groups', function() { it('Responds with the groups', function(done) { @@ -183,15 +188,32 @@ describe('API tests', function() { .end(done); }); }); - - describe('Create new group', function() { - it('Responds with the group', function(done) { + + describe('Delete group', function() { + it('Responds with the deleted system', function(done) { request - .post('/api/systems') - .send({ name: getRandomString() }) + .delete('/api/systems/' + mockGroup.name + token) .expect(200) .end(done); }); - }); + }); + + describe('Retrieve users', function() { + it('Responds with the users', function(done) { + request + .get('/api/users') + .expect(200) + .end(done); + }); + }); + + describe('Delete user', function() { + it('Responds with the deleted user', function(done) { + request + .delete('/api/users/' + mockUser.username + token) + .expect(200) + .end(done); + }); + }); }); From c5cf21102cf38db01044f469f147fab0c34f5b23 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Mon, 29 Oct 2018 14:34:34 -0400 Subject: [PATCH 06/20] add UI components for match template --- controllers/serviceController.js | 2 ++ public/js/app/controllers.js | 3 ++- public/js/app/services.js | 1 + public/partials/addapiform.html | 12 +++++++++--- public/partials/updateForm.html | 11 +++++++++-- routes/virtual.js | 1 - 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/controllers/serviceController.js b/controllers/serviceController.js index ccccaa9a..13e32fe3 100644 --- a/controllers/serviceController.js +++ b/controllers/serviceController.js @@ -147,6 +147,7 @@ function addService(req, res) { type: req.body.type, delay: req.body.delay, basePath: '/' + req.body.sut.name + req.body.basePath, + matchTemplate: req.body.matchTemplate, rrpairs: req.body.rrpairs }; @@ -193,6 +194,7 @@ function updateService(req, res) { } // don't let consumer alter name, base path, etc. + service.matchTemplate = req.body.matchTemplate; service.rrpairs = req.body.rrpairs; if (req.body.delay) service.delay = req.body.delay; diff --git a/public/js/app/controllers.js b/public/js/app/controllers.js index a201a5ec..7f32e7fa 100644 --- a/public/js/app/controllers.js +++ b/public/js/app/controllers.js @@ -155,7 +155,8 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','ngFileSaver type: service.type, delay: service.delay, txnCount: service.txnCount, - basePath: service.basePath + basePath: service.basePath, + matchTemplate: service.matchTemplate }; $scope.servicevo.rawpairs = []; diff --git a/public/js/app/services.js b/public/js/app/services.js index c22a4bf4..ddb28d86 100644 --- a/public/js/app/services.js +++ b/public/js/app/services.js @@ -258,6 +258,7 @@ var serv = angular.module('mockapp.services',['mockapp.factories']) basePath: '/' + servicevo.basePath, type: servicevo.type, delay: servicevo.delay, + matchTemplate: servicevo.matchTemplate, rrpairs: rrpairs }; diff --git a/public/partials/addapiform.html b/public/partials/addapiform.html index 2a2eca65..55736974 100644 --- a/public/partials/addapiform.html +++ b/public/partials/addapiform.html @@ -23,7 +23,7 @@

Mock a REST or SOAP Service

- +

Check to create new Group @@ -50,12 +50,19 @@

Mock a REST or SOAP Service

- +
+
+ +
+ +
+
+

@@ -208,7 +215,6 @@

Request / Response Pair

-
diff --git a/public/partials/updateForm.html b/public/partials/updateForm.html index 7397eadf..ee60e959 100644 --- a/public/partials/updateForm.html +++ b/public/partials/updateForm.html @@ -46,11 +46,18 @@

Update Mock Service

- +
-
+
+ +
+ +
+ +
+
diff --git a/routes/virtual.js b/routes/virtual.js index 230a3d47..59de73a1 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -49,7 +49,6 @@ function registerRRPair(service, rrpair) { function matchRequest(payload) { let reqData; - //const isGet = req.method === 'GET'; if (rrpair.reqData) { if (rrpair.payloadType === 'XML') { xml2js.parseString(rrpair.reqData, function(err, data) { From be778388986b9b4cd99edf50422827407bf44167 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Tue, 30 Oct 2018 10:25:20 -0400 Subject: [PATCH 07/20] expand test coverage to OpenAPI and WSDL --- examples/hello-service.wsdl | 51 ++++++++++++++++++++++++ tests/test.js | 79 +++++++++++++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 examples/hello-service.wsdl diff --git a/examples/hello-service.wsdl b/examples/hello-service.wsdl new file mode 100644 index 00000000..f2cdb50d --- /dev/null +++ b/examples/hello-service.wsdl @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WSDL File for HelloService + + + + + \ No newline at end of file diff --git a/tests/test.js b/tests/test.js index 649224bb..96ee8a9f 100644 --- a/tests/test.js +++ b/tests/test.js @@ -8,11 +8,26 @@ const YAML = require('yamljs'); let id = ''; let token = '?token='; -const resource = '/api/services'; +const resource = '/api/services'; +const swagService = './api-docs.yml'; +const oasService = './examples/petstore.yaml'; +const wsdlService = './examples/hello-service.wsdl'; const restService = require('../examples/rest-json-example.json'); const soapService = require('../examples/soap-example.json'); -const swagService = YAML.load('./api-docs.yml'); -const oasService = require('../examples/petstore.json'); + +const oasQuery = { + 'type': 'openapi', + 'base': '/oas/test', + 'name': 'oas-test', + 'group': 'test' +}; + +const wsdlQuery = { + 'type': 'wsdl', + 'base': '/wsdl/test', + 'name': 'wsdl-test', + 'group': 'test' +} const mockUser = { username: getRandomString(), @@ -170,6 +185,64 @@ describe('API tests', function() { }); }); + describe('Create service from WSDL', function() { + it('Responds with the new service', function(done) { + request + .post(resource + '/fromSpec' + token) + .query(wsdlQuery) + .attach('spec', wsdlService) + .send() + .expect(200) + .expect(function(res) { + id = res.body._id; + }) + .end(done); + }); + }); + + describe('Delete WSDL service', function() { + it('Responds with the deleted service', function(done) { + request + .delete(resource + '/' + id + token) + .expect(200) + .end(done); + }); + }); + + describe('Create service from OpenAPI', function() { + it('Rejects Swagger 2', function(done) { + request + .post(resource + '/fromSpec' + token) + .query(oasQuery) + .attach('spec', swagService) + .send() + .expect(400) + .end(done); + }); + + it('Accepts OpenAPI 3', function(done) { + request + .post(resource + '/fromSpec' + token) + .query(oasQuery) + .attach('spec', oasService) + .send() + .expect(200) + .expect(function(res) { + id = res.body._id; + }) + .end(done); + }); + }); + + describe('Delete OpenAPI service', function() { + it('Responds with the deleted service', function(done) { + request + .delete(resource + '/' + id + token) + .expect(200) + .end(done); + }); + }); + describe('Create new group', function() { it('Responds with the group', function(done) { request From 60c050ef1807667d2cde7958a6851be584e77c72 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Tue, 30 Oct 2018 14:09:01 -0400 Subject: [PATCH 08/20] backend changes to support multiple match templates --- controllers/serviceController.js | 14 ++++++++------ models/Service.js | 2 +- public/js/app/controllers.js | 13 +++++++++++-- public/js/app/services.js | 2 +- public/partials/addapiform.html | 4 ++-- public/partials/updateForm.html | 4 ++-- routes/virtual.js | 9 +++++++-- tests/test.js | 18 +++++++++--------- 8 files changed, 41 insertions(+), 25 deletions(-) diff --git a/controllers/serviceController.js b/controllers/serviceController.js index a12bfcea..e5963d20 100644 --- a/controllers/serviceController.js +++ b/controllers/serviceController.js @@ -66,17 +66,19 @@ function searchDuplicate(service, next) { name: { $ne: service.name }, basePath: service.basePath }; + const query = { name: service.name, basePath: service.basePath }; - Service.findOne(query2ServDiffNmSmBP, function (err, sameNmDupBP) { + + Service.findOne(query2ServDiffNmSmBP, function(err, sameNmDupBP) { if (err) { handleError(err, res, 500); return; } else if (sameNmDupBP) - next({ "twoServDiffNmSmBP": "yes" }); + next({ twoServDiffNmSmBP: true }); else { Service.findOne(query, function (err, duplicate) { if (err) { @@ -159,14 +161,14 @@ function addService(req, res) { type: req.body.type, delay: req.body.delay, basePath: '/' + req.body.sut.name + req.body.basePath, - matchTemplate: req.body.matchTemplate, + matchTemplates: req.body.matchTemplates, rrpairs: req.body.rrpairs }; searchDuplicate(serv, function(duplicate) { - if(duplicate && duplicate.twoServDiffNmSmBP && duplicate.twoServDiffNmSmBP=='yes'){ + if (duplicate && duplicate.twoServDiffNmSmBP){ res.json({"error":"twoSeviceDiffNameSameBasePath"}); - return 'twoSeviceDiffNameSameBasePath'; + return; } else if (duplicate) { // merge services @@ -206,7 +208,7 @@ function updateService(req, res) { } // don't let consumer alter name, base path, etc. - service.matchTemplate = req.body.matchTemplate; + service.matchTemplates = req.body.matchTemplates; service.rrpairs = req.body.rrpairs; if (req.body.delay) service.delay = req.body.delay; diff --git a/models/Service.js b/models/Service.js index eeaf3fbb..e40212e2 100644 --- a/models/Service.js +++ b/models/Service.js @@ -16,7 +16,7 @@ const serviceSchema = new mongoose.Schema({ type: String, index: true }, - matchTemplate: mongoose.Schema.Types.Mixed, + matchTemplates: [mongoose.Schema.Types.Mixed], rrpairs: [RRPair.schema], delay: { // force integer only diff --git a/public/js/app/controllers.js b/public/js/app/controllers.js index a041e748..c03fa90e 100644 --- a/public/js/app/controllers.js +++ b/public/js/app/controllers.js @@ -34,6 +34,7 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac function($scope,apiHistoryService,sutService,suggestionsService, helperFactory){ $scope.sutlist = sutService.getAllSUT(); $scope.servicevo = {}; + $scope.servicevo.matchTemplates = ['']; $scope.servicevo.rawpairs = [{ id: 0, queriesArr: [{ @@ -155,11 +156,19 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac type: service.type, delay: service.delay, txnCount: service.txnCount, - basePath: service.basePath, - matchTemplate: service.matchTemplate + basePath: service.basePath }; + $scope.servicevo.matchTemplates = []; $scope.servicevo.rawpairs = []; + + if (service.matchTemplates.length) { + $scope.servicevo.matchTemplates.push(service.matchTemplates[0]); + } + else { + $scope.servicevo.matchTemplates.push(''); + } + var rrid = 0; service.rrpairs.forEach(function(rr){ rr.id = rrid; diff --git a/public/js/app/services.js b/public/js/app/services.js index 426a17c6..c430abb8 100644 --- a/public/js/app/services.js +++ b/public/js/app/services.js @@ -276,7 +276,7 @@ var serv = angular.module('mockapp.services',['mockapp.factories']) basePath: '/' + servicevo.basePath, type: servicevo.type, delay: servicevo.delay, - matchTemplate: servicevo.matchTemplate, + matchTemplates: servicevo.matchTemplates, rrpairs: rrpairs }; diff --git a/public/partials/addapiform.html b/public/partials/addapiform.html index 55736974..98c5877c 100644 --- a/public/partials/addapiform.html +++ b/public/partials/addapiform.html @@ -57,9 +57,9 @@

Mock a REST or SOAP Service

- +
- +
diff --git a/public/partials/updateForm.html b/public/partials/updateForm.html index ee60e959..f1c6cb59 100644 --- a/public/partials/updateForm.html +++ b/public/partials/updateForm.html @@ -53,9 +53,9 @@

Update Mock Service

- +
- +
diff --git a/routes/virtual.js b/routes/virtual.js index 59de73a1..2f552097 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -48,6 +48,7 @@ function registerRRPair(service, rrpair) { // function for matching requests to responses function matchRequest(payload) { let reqData; + let match = false; if (rrpair.reqData) { if (rrpair.payloadType === 'XML') { @@ -61,8 +62,12 @@ function registerRRPair(service, rrpair) { } // match request body based on template - let match = false; - let template = service.matchTemplate; + let template; + let templates = service.matchTemplates; + + if (templates.length) { + template = templates[0]; + } if (template) { if (rrpair.payloadType === 'XML') { diff --git a/tests/test.js b/tests/test.js index 96ee8a9f..fdfd33f4 100644 --- a/tests/test.js +++ b/tests/test.js @@ -16,18 +16,18 @@ const restService = require('../examples/rest-json-example.json'); const soapService = require('../examples/soap-example.json'); const oasQuery = { - 'type': 'openapi', - 'base': '/oas/test', - 'name': 'oas-test', - 'group': 'test' + type: 'openapi', + base: '/oas/test', + name: 'oas-test', + group: 'test' }; const wsdlQuery = { - 'type': 'wsdl', - 'base': '/wsdl/test', - 'name': 'wsdl-test', - 'group': 'test' -} + type: 'wsdl', + base: '/wsdl/test', + name: 'wsdl-test', + group: 'test' +}; const mockUser = { username: getRandomString(), From fd0a58722c28090fe8fcf8b2e82b9d4ff8617272 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Tue, 30 Oct 2018 14:42:00 -0400 Subject: [PATCH 09/20] populate match templates from WSDL --- lib/wsdl/parser.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/wsdl/parser.js b/lib/wsdl/parser.js index 13e0f0ca..05a80d9f 100644 --- a/lib/wsdl/parser.js +++ b/lib/wsdl/parser.js @@ -12,6 +12,7 @@ function parse(wsdl) { return new Promise(function(resolve, reject) { const serv = new Service(); serv.type = 'SOAP'; + serv.matchTemplates = []; soap.createClientAsync(wsdl, function(err, client) { if (err) return reject(err); @@ -49,7 +50,11 @@ function parse(wsdl) { const inName = methods[j].input.$name; const inMsg = messages[inName]; const reqXml = toXml(inMsg.parts); - rr.reqData = pd.xml(`<${inName}>${reqXml}`); + const fullXml = pd.xml(`<${inName}>${reqXml}`); + rr.reqData = fullXml; + + // create a new match template + serv.matchTemplates.push(fullXml); // extract and set res data const outName = methods[j].output.$name; From d017b95616a00b5fafd5f3b6a5b7fa2a6fc6c061 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 09:47:01 -0400 Subject: [PATCH 10/20] try to match against multiple templates --- routes/virtual.js | 49 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/routes/virtual.js b/routes/virtual.js index 2f552097..922ce602 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -62,35 +62,34 @@ function registerRRPair(service, rrpair) { } // match request body based on template - let template; let templates = service.matchTemplates; - - if (templates.length) { - template = templates[0]; - } - - if (template) { - if (rrpair.payloadType === 'XML') { - xml2js.parseString(template, function(err, xmlTemplate) { - template = xmlTemplate; - }); - } - - const flatTemplate = flattenObject(template); - const flatPayload = flattenObject(payload); - const flatReqData = flattenObject(reqData); - const trimmedPayload = {}; const trimmedReqData = {}; + if (templates && templates.length) { + for (let template of templates) { + if (rrpair.payloadType === 'XML') { + xml2js.parseString(template, function(err, xmlTemplate) { + template = xmlTemplate; + }); + } + + const flatTemplate = flattenObject(template); + const flatPayload = flattenObject(payload); + const flatReqData = flattenObject(reqData); + + const trimmedPayload = {}; const trimmedReqData = {}; + + for (let field in flatTemplate) { + trimmedPayload[field] = flatPayload[field]; + trimmedReqData[field] = flatReqData[field]; + } + + debug(JSON.stringify(trimmedPayload, null, 2)); + debug(JSON.stringify(trimmedReqData, null, 2)); + + match = deepEquals(trimmedPayload, trimmedReqData); - for (let field in flatTemplate) { - trimmedPayload[field] = flatPayload[field]; - trimmedReqData[field] = flatReqData[field]; + if (match) break; } - - debug(JSON.stringify(trimmedPayload, null, 2)); - debug(JSON.stringify(trimmedReqData, null, 2)); - - match = deepEquals(trimmedPayload, trimmedReqData); } // else match against all fields else { From 65c9af1a5aa8c6ff5d2e7118252af7c6bc1bc83f Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 10:58:11 -0400 Subject: [PATCH 11/20] update API documentation --- api-docs.yml | 951 ++++++++++++++++++++----------- controllers/serviceController.js | 1 - examples/petstore.json | 175 ------ examples/petstore.yaml | 109 ---- tests/test.js | 26 +- 5 files changed, 615 insertions(+), 647 deletions(-) delete mode 100755 examples/petstore.json delete mode 100644 examples/petstore.yaml diff --git a/api-docs.yml b/api-docs.yml index 3dc0e708..a6ec540c 100644 --- a/api-docs.yml +++ b/api-docs.yml @@ -1,347 +1,604 @@ ---- - swagger: "2.0" - info: - version: "1.0.0" - title: "Mockiato" - basePath: "/api" - schemes: - - "http" - consumes: - - "application/json" - produces: - - "application/json" - paths: - /login: - post: - description: "returns an access token for the user" - operationId: "getAccessToken" - tags: - - auth - produces: - - "application/json" - parameters: - - - name: "authReq" - in: "body" - description: "your MS credentials" - required: true - schema: - $ref: "#/definitions/AuthReq" - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/AuthRes" - "401": - description: "authentication failure" - schema: - $ref: "#/definitions/AuthRes" - /services: - post: - description: "creates a new virtual service" - operationId: "addVirtualService" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "service" - description: "the service to create" - in: "body" - schema: - $ref: "#/definitions/VirtualService" - - - name: "x-access-token" - description: "your access token" - in: "header" - type: string - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/VirtualService" - /services/{id}: - get: - description: "retrieves a virtual service" - operationId: "getVirtualServiceByID" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "id" - in: "path" - description: "ID of service to fetch" - required: true - type: integer - format: "int64" - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/VirtualService" - put: - description: "updates a virtual service" - operationId: "updateVirtualService" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "id" - in: "path" - description: "ID of service to update" - required: true - type: integer - format: "int64" - - - name: "service" - description: "the service to update" - in: "body" - schema: - $ref: "#/definitions/VirtualService" - - - name: "x-access-token" - description: "your access token" - in: "header" - type: string - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/VirtualService" - delete: - description: "removes a virtual service" - operationId: "removeVirtualService" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "id" - in: "path" - description: "ID of service to fetch" - required: true - type: integer - format: "int64" - - - name: "x-access-token" - description: "your access token" - in: "header" - type: string - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/VirtualService" - /api/services/{id}/toggle: - post: - description: "toggles a virtual service on / off" - operationId: "toggleVirtualService" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "id" - in: "path" - description: "ID of service to update" - required: true - type: integer - format: "int64" - - - name: "x-access-token" - description: "your access token" - in: "header" - type: string - responses: - "200": - description: "toggle response" - schema: - $ref: "#/definitions/ToggleResponse" - /services/sut/{name}: - get: - description: "retrieves virtual services by system under test" - operationId: "getVirtualServicesBySUT" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "name" - in: "path" - description: "name of system under test" - required: true - type: string - responses: - "200": - description: "successful response" - schema: - type: array - items: - type: object - $ref: "#/definitions/VirtualService" - /services/user/{name}: - get: - description: "retrieves virtual services by owner" - operationId: "getVirtualServicesByOwner" - tags: - - services - produces: - - "application/json" - parameters: - - - name: "name" - in: "path" - description: "name of user / owner" - required: true - type: string - responses: - "200": - description: "successful response" - schema: - type: array - items: - type: object - $ref: "#/definitions/VirtualService" - /systems: - get: - description: "retrieves all systems under test" - operationId: "getSystemsUnderTest" - tags: - - systems - produces: - - "application/json" - responses: - "200": - description: "successful response" - schema: - type: array - items: - type: object - $ref: "#/definitions/SystemUnderTest" - post: - description: "creates a new system under test" - operationId: "addSystemUnderTest" - tags: - - systems - produces: - - "application/json" - parameters: - - - name: "sut" - description: "the SUT to create" - in: "body" - schema: - $ref: "#/definitions/SystemUnderTest" - responses: - "200": - description: "successful response" - schema: - $ref: "#/definitions/SystemUnderTest" - /users: - get: - description: "retrieves all users" - operationId: "getUsers" - tags: - - users - produces: - - "application/json" - responses: - "200": - description: "successful response" - schema: - type: array - items: - type: object - $ref: "#/definitions/UserDetails" - - definitions: - AuthReq: - type: object - properties: - username: - type: string - required: true - password: - type: string - required: true - AuthRes: - type: object - properties: - success: - type: boolean - token: - type: string - message: - type: string - VirtualService: - type: object - properties: - sut: - type: object - allOf: - - $ref: "#/definitions/SystemUnderTest" - user: - type: object - allOf: - - $ref: "#/definitions/UserDetails" - name: - type: string - type: - type: string - basePath: - type: string - rrpairs: - type: array - items: - type: object - $ref: "#/definitions/ReqResPair" - running: - type: boolean - SystemUnderTest: - type: object - properties: - name: - type: string - ReqResPair: - type: object - properties: - verb: - type: string - path: - type: string - payloadType: - type: string - reqHeaders: - type: object - reqData: - type: object - resStatus: - type: integer - resHeaders: - type: object - resData: - type: object - UserDetails: - type: object - properties: - uid: - type: string - mail: - type: string - ToggleResponse: - type: object - properties: - message: - type: string - service: - type: object - allOf: - - $ref: "#/definitions/VirtualService" +openapi: 3.0.0 +info: + title: Mockiato + description: '' + contact: {} + version: '1.0.0' +servers: +- url: '{mockiatoUrl}' + variables: + mockiatoUrl: + default: http://mockiato.optum.com/api +paths: + /login: + post: + tags: + - auth + summary: getAccessToken + description: returns an access token for the user + operationId: GetAccessToken + parameters: [] + requestBody: + description: your MS credentials + content: + application/json: + schema: + description: your MS credentials + $ref: '#/components/schemas/AuthReq' + required: true + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/AuthRes' + 401: + description: authentication failure + content: + application/json: + schema: + $ref: '#/components/schemas/AuthRes' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /services: + post: + tags: + - services + summary: addVirtualService + description: creates a new virtual service + operationId: AddVirtualService + parameters: + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + requestBody: + description: the service to create + content: + application/json: + schema: + description: the service to create + $ref: '#/components/schemas/VirtualService' + required: false + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/VirtualService' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /services/fromSpec: + post: + tags: + - services + summary: createVirtualServiceFromSpec + description: creates a virtual service from OpenAPI or WSDL + operationId: CreateVirtualServiceFromSpec + parameters: + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + - name: name + in: query + description: name of service to create + required: true + style: simple + explode: false + schema: + type: string + - name: type + in: query + description: type of spec (e.g. openapi, wsdl) + required: true + style: simple + explode: false + schema: + type: string + - name: group + in: query + description: group to add the service to + required: true + style: simple + explode: false + schema: + type: string + - name: url + in: query + description: the url to the spec + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/VirtualService' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /services/{id}: + get: + tags: + - services + summary: getVirtualServiceByID + description: retrieves a virtual service + operationId: GetVirtualServiceByID + parameters: + - name: id + in: path + description: ID of service to fetch + required: true + style: simple + explode: false + schema: + type: integer + format: int64 + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/VirtualService' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + put: + tags: + - services + summary: updateVirtualService + description: updates a virtual service + operationId: UpdateVirtualService + parameters: + - name: id + in: path + description: ID of service to update + required: true + style: simple + explode: false + schema: + type: integer + format: int64 + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + requestBody: + description: the service to update + content: + application/json: + schema: + description: the service to update + $ref: '#/components/schemas/VirtualService' + required: false + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/VirtualService' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + delete: + tags: + - services + summary: removeVirtualService + description: removes a virtual service + operationId: RemoveVirtualService + parameters: + - name: id + in: path + description: ID of service to fetch + required: true + style: simple + explode: false + schema: + type: integer + format: int64 + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/VirtualService' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /api/services/{id}/toggle: + post: + tags: + - services + summary: toggleVirtualService + description: toggles a virtual service on / off + operationId: ToggleVirtualService + parameters: + - name: id + in: path + description: ID of service to update + required: true + style: simple + explode: false + schema: + type: integer + format: int64 + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + responses: + 200: + description: toggle response + content: + application/json: + schema: + description: toggle response + $ref: '#/components/schemas/ToggleResponse' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /services/sut/{name}: + get: + tags: + - services + summary: getVirtualServicesBySUT + description: retrieves virtual services by system under test + operationId: GetVirtualServicesBySUT + parameters: + - name: name + in: path + description: name of system under test + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/VirtualService' + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /services/user/{name}: + get: + tags: + - services + summary: getVirtualServicesByOwner + description: retrieves virtual services by owner + operationId: GetVirtualServicesByOwner + parameters: + - name: name + in: path + description: name of user / owner + required: true + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/VirtualService' + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /systems: + get: + tags: + - systems + summary: getSystemsUnderTest + description: retrieves all systems under test + operationId: GetSystemsUnderTest + parameters: [] + responses: + 200: + description: successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SystemUnderTest' + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + post: + tags: + - systems + summary: addSystemUnderTest + description: creates a new system under test + operationId: AddSystemUnderTest + parameters: [] + requestBody: + description: the SUT to create + content: + application/json: + schema: + description: the SUT to create + $ref: '#/components/schemas/SystemUnderTest' + required: false + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + $ref: '#/components/schemas/SystemUnderTest' + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + delete: + tags: + - systems + summary: removeSystemUnderTest + description: removes a system under test + operationId: RemoveSystemUnderTest + parameters: + - name: name + in: path + description: name of system to remove + required: true + style: simple + explode: false + schema: + type: string + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + /users: + get: + tags: + - users + summary: getUsers + description: retrieves all users + operationId: GetUsers + parameters: [] + responses: + 200: + description: successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserDetails' + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false + delete: + tags: + - users + summary: removeUser + description: removes a system under test + operationId: RemoveSystemUnderTest + parameters: + - name: name + in: path + description: uid of user to remove + required: true + style: simple + explode: false + schema: + type: string + - name: x-access-token + in: header + required: true + description: your access token + style: simple + explode: false + schema: + type: string + responses: + 200: + description: successful response + content: + application/json: + schema: + description: successful response + x-operation-settings: + CollectParameters: false + AllowDynamicQueryParameters: false + AllowDynamicFormParameters: false + IsMultiContentStreaming: false +components: + schemas: + AuthReq: + title: AuthReq + required: + - username + - password + type: object + properties: + username: + type: string + password: + type: string + AuthRes: + title: AuthRes + type: object + properties: + success: + type: boolean + token: + type: string + message: + type: string + VirtualService: + title: VirtualService + type: object + properties: + sut: + $ref: '#/components/schemas/SystemUnderTest' + user: + $ref: '#/components/schemas/UserDetails' + name: + type: string + type: + type: string + basePath: + type: string + matchTemplates: + type: array + items: + type: string + rrpairs: + type: array + items: + $ref: '#/components/schemas/ReqResPair' + description: '' + delay: + type: integer + format: int64 + running: + type: boolean + SystemUnderTest: + title: SystemUnderTest + type: object + properties: + name: + type: string + ReqResPair: + title: ReqResPair + type: object + properties: + verb: + type: string + path: + type: string + payloadType: + type: string + reqHeaders: + type: object + reqData: + type: object + resStatus: + type: integer + format: int64 + resHeaders: + type: object + resData: + type: object + UserDetails: + title: UserDetails + type: object + properties: + uid: + type: string + mail: + type: string + ToggleResponse: + title: ToggleResponse + type: object + properties: + message: + type: string + service: + $ref: '#/components/schemas/VirtualService' +tags: +- name: auth + description: '' +- name: services + description: '' +- name: systems + description: '' +- name: users + description: '' diff --git a/controllers/serviceController.js b/controllers/serviceController.js index e5963d20..eaad6550 100644 --- a/controllers/serviceController.js +++ b/controllers/serviceController.js @@ -287,7 +287,6 @@ function isYaml(req) { function createFromSpec(req, res) { const type = req.query.type; - const base = req.query.base; const name = req.query.name; const url = req.query.url; const sut = { name: req.query.group }; diff --git a/examples/petstore.json b/examples/petstore.json deleted file mode 100755 index 73a24705..00000000 --- a/examples/petstore.json +++ /dev/null @@ -1,175 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "version": "1.0.0", - "title": "Swagger Petstore", - "license": { - "name": "MIT" - } - }, - "servers": [ - { - "url": "http://petstore.swagger.io/v1" - } - ], - "paths": { - "/pets": { - "get": { - "summary": "List all pets", - "operationId": "listPets", - "tags": [ - "pets" - ], - "parameters": [ - { - "name": "limit", - "in": "query", - "description": "How many items to return at one time (max 100)", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], - "responses": { - "200": { - "description": "A paged array of pets", - "headers": { - "x-next": { - "description": "A link to the next page of responses", - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pets" - } - } - } - }, - "default": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - }, - "post": { - "summary": "Create a pet", - "operationId": "createPets", - "tags": [ - "pets" - ], - "responses": { - "201": { - "description": "Null response" - }, - "default": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/pets/{petId}": { - "get": { - "summary": "Info for a specific pet", - "operationId": "showPetById", - "tags": [ - "pets" - ], - "parameters": [ - { - "name": "petId", - "in": "path", - "required": true, - "description": "The id of the pet to retrieve", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "Expected response to a valid request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Pets" - } - } - } - }, - "default": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "Pet": { - "required": [ - "id", - "name" - ], - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "tag": { - "type": "string" - } - } - }, - "Pets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pet" - } - }, - "Error": { - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - } - } - } - } - } -} \ No newline at end of file diff --git a/examples/petstore.yaml b/examples/petstore.yaml deleted file mode 100644 index 09941de9..00000000 --- a/examples/petstore.yaml +++ /dev/null @@ -1,109 +0,0 @@ -openapi: "3.0.0" -info: - version: 1.0.0 - title: Swagger Petstore - license: - name: MIT -servers: - - url: http://petstore.swagger.io/v1 -paths: - /pets: - get: - summary: List all pets - operationId: listPets - tags: - - pets - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - format: int32 - responses: - '200': - description: A paged array of pets - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - post: - summary: Create a pet - operationId: createPets - tags: - - pets - responses: - '201': - description: Null response - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - /pets/{petId}: - get: - summary: Info for a specific pet - operationId: showPetById - tags: - - pets - parameters: - - name: petId - in: path - required: true - description: The id of the pet to retrieve - schema: - type: string - responses: - '200': - description: Expected response to a valid request - content: - application/json: - schema: - $ref: "#/components/schemas/Pets" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" -components: - schemas: - Pet: - required: - - id - - name - properties: - id: - type: integer - format: int64 - name: - type: string - tag: - type: string - Pets: - type: array - items: - $ref: "#/components/schemas/Pet" - Error: - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string diff --git a/tests/test.js b/tests/test.js index fdfd33f4..c91b80ce 100644 --- a/tests/test.js +++ b/tests/test.js @@ -9,22 +9,19 @@ let id = ''; let token = '?token='; const resource = '/api/services'; -const swagService = './api-docs.yml'; -const oasService = './examples/petstore.yaml'; +const oasService = './api-docs.yml'; const wsdlService = './examples/hello-service.wsdl'; const restService = require('../examples/rest-json-example.json'); const soapService = require('../examples/soap-example.json'); const oasQuery = { type: 'openapi', - base: '/oas/test', name: 'oas-test', group: 'test' }; const wsdlQuery = { type: 'wsdl', - base: '/wsdl/test', name: 'wsdl-test', group: 'test' }; @@ -50,6 +47,15 @@ describe('API tests', function() { app.on('started', done); }); + describe('Get API docs', function() { + it('Serves the documentation', function(done) { + request + .get('/api-docs') + .expect(303) + .end(done); + }); + }); + describe('Register new user', function() { it('Redirects to login', function(done) { request @@ -210,17 +216,7 @@ describe('API tests', function() { }); describe('Create service from OpenAPI', function() { - it('Rejects Swagger 2', function(done) { - request - .post(resource + '/fromSpec' + token) - .query(oasQuery) - .attach('spec', swagService) - .send() - .expect(400) - .end(done); - }); - - it('Accepts OpenAPI 3', function(done) { + it('Responds with the new service', function(done) { request .post(resource + '/fromSpec' + token) .query(oasQuery) From 76d644b9b01f77d52843d39681662d872f8bc6bb Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 11:11:40 -0400 Subject: [PATCH 12/20] update API documentation, contd --- api-docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/api-docs.yml b/api-docs.yml index a6ec540c..8da3b0eb 100644 --- a/api-docs.yml +++ b/api-docs.yml @@ -249,7 +249,6 @@ paths: application/json: schema: description: successful response - $ref: '#/components/schemas/VirtualService' x-operation-settings: CollectParameters: false AllowDynamicQueryParameters: false From a1b0cb5d4effb53ca9421dafe299b7e15c23cc5e Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 11:12:37 -0400 Subject: [PATCH 13/20] update API documentation, contd --- api-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-docs.yml b/api-docs.yml index 8da3b0eb..0d68a363 100644 --- a/api-docs.yml +++ b/api-docs.yml @@ -227,7 +227,7 @@ paths: parameters: - name: id in: path - description: ID of service to fetch + description: ID of service to remove required: true style: simple explode: false From 69eda55bd6ba314b5df307def0f3da72950f5580 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 11:43:06 -0400 Subject: [PATCH 14/20] multiple matching templates (frontend) --- public/js/app/controllers.js | 6 ++++-- public/partials/updateForm.html | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/public/js/app/controllers.js b/public/js/app/controllers.js index c03fa90e..df6185e6 100644 --- a/public/js/app/controllers.js +++ b/public/js/app/controllers.js @@ -162,8 +162,10 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac $scope.servicevo.matchTemplates = []; $scope.servicevo.rawpairs = []; - if (service.matchTemplates.length) { - $scope.servicevo.matchTemplates.push(service.matchTemplates[0]); + if (service.matchTemplates && service.matchTemplates.length) { + service.matchTemplates.forEach(function(template) { + $scope.servicevo.matchTemplates.push(template); + }); } else { $scope.servicevo.matchTemplates.push(''); diff --git a/public/partials/updateForm.html b/public/partials/updateForm.html index f1c6cb59..c8938d9d 100644 --- a/public/partials/updateForm.html +++ b/public/partials/updateForm.html @@ -53,12 +53,16 @@

Update Mock Service

- +
- +
+ +
+
+

From f06c535e3ad46530a5132a214155f1d024265796 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Wed, 31 Oct 2018 11:55:13 -0400 Subject: [PATCH 15/20] multiple matching templates (frontend), contd --- public/js/app/controllers.js | 8 ++++++++ public/partials/updateForm.html | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/public/js/app/controllers.js b/public/js/app/controllers.js index df6185e6..47e59ed6 100644 --- a/public/js/app/controllers.js +++ b/public/js/app/controllers.js @@ -254,6 +254,14 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac }; this.getService(); + $scope.addTemplate = function() { + $scope.servicevo.matchTemplates.push(''); + }; + + $scope.removeTemplate = function(index) { + $scope.servicevo.matchTemplates.splice(index, 1); + }; + $scope.addNewRRPair = function() { var newItemNo = $scope.servicevo.rawpairs.length; $scope.servicevo.rawpairs.push({ diff --git a/public/partials/updateForm.html b/public/partials/updateForm.html index c8938d9d..1ae9db2c 100644 --- a/public/partials/updateForm.html +++ b/public/partials/updateForm.html @@ -56,8 +56,23 @@

Update Mock Service

- -
+
+
+
+
+ +
+ +
+ +
+ +
+
From 0ed86d6cef85c45603c84c96fe595bc7ed245e3a Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Thu, 1 Nov 2018 09:45:18 -0400 Subject: [PATCH 16/20] fixes for match template feature --- public/js/app/controllers.js | 22 +++++++++++++++------- public/js/app/services.js | 7 ++++++- public/partials/addapiform.html | 26 ++++++++++++++++++++++---- public/partials/updateForm.html | 2 +- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/public/js/app/controllers.js b/public/js/app/controllers.js index 47e59ed6..308aa443 100644 --- a/public/js/app/controllers.js +++ b/public/js/app/controllers.js @@ -34,7 +34,7 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac function($scope,apiHistoryService,sutService,suggestionsService, helperFactory){ $scope.sutlist = sutService.getAllSUT(); $scope.servicevo = {}; - $scope.servicevo.matchTemplates = ['']; + $scope.servicevo.matchTemplates = [{ id: 0, val: '' }]; $scope.servicevo.rawpairs = [{ id: 0, queriesArr: [{ @@ -48,14 +48,22 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac }] }]; + $scope.statusCodes = suggestionsService.getStatusCodes(); + $scope.possibleHeaders = suggestionsService.getPossibleHeaders(); + $scope.dropdown = function() { if($scope.sutChecked == false){ $scope.sutlist = sutService.getAllSUT(); } }; - $scope.statusCodes = suggestionsService.getStatusCodes(); - $scope.possibleHeaders = suggestionsService.getPossibleHeaders(); + $scope.addTemplate = function() { + $scope.servicevo.matchTemplates.push({ id: 0, val: '' }); + }; + + $scope.removeTemplate = function(index) { + $scope.servicevo.matchTemplates.splice(index, 1); + }; $scope.addNewRRPair = function() { var newItemNo = $scope.servicevo.rawpairs.length; @@ -163,12 +171,12 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac $scope.servicevo.rawpairs = []; if (service.matchTemplates && service.matchTemplates.length) { - service.matchTemplates.forEach(function(template) { - $scope.servicevo.matchTemplates.push(template); + service.matchTemplates.forEach(function(template, index) { + $scope.servicevo.matchTemplates.push({ id: index, val: template }); }); } else { - $scope.servicevo.matchTemplates.push(''); + $scope.servicevo.matchTemplates.push({ id: 0, val: '' }); } var rrid = 0; @@ -255,7 +263,7 @@ var ctrl = angular.module("mockapp.controllers",['mockapp.services','mockapp.fac this.getService(); $scope.addTemplate = function() { - $scope.servicevo.matchTemplates.push(''); + $scope.servicevo.matchTemplates.push({ id: 0, val: '' }); }; $scope.removeTemplate = function(index) { diff --git a/public/js/app/services.js b/public/js/app/services.js index c430abb8..17f0b2dd 100644 --- a/public/js/app/services.js +++ b/public/js/app/services.js @@ -270,13 +270,18 @@ var serv = angular.module('mockapp.services',['mockapp.factories']) rrpairs.push(rrpair); }); + var templates = []; + servicevo.matchTemplates.forEach(function(template) { + templates.push(template.val); + }); + var servData = { sut: { name: servicevo.sut.name }, name: servicevo.name, basePath: '/' + servicevo.basePath, type: servicevo.type, delay: servicevo.delay, - matchTemplates: servicevo.matchTemplates, + matchTemplates: templates, rrpairs: rrpairs }; diff --git a/public/partials/addapiform.html b/public/partials/addapiform.html index 98c5877c..75c03c14 100644 --- a/public/partials/addapiform.html +++ b/public/partials/addapiform.html @@ -57,11 +57,29 @@

Mock a REST or SOAP Service

- -
- + +
+
+
+
+
+
+ +
+ +
+ +
+ +
+
+
+
-
diff --git a/public/partials/updateForm.html b/public/partials/updateForm.html index 1ae9db2c..e02bc523 100644 --- a/public/partials/updateForm.html +++ b/public/partials/updateForm.html @@ -58,7 +58,7 @@

Update Mock Service

-
+
From 08666ec82405d9432633ac89f35685ded0de95cd Mon Sep 17 00:00:00 2001 From: HymaSree Date: Thu, 1 Nov 2018 19:34:17 +0530 Subject: [PATCH 17/20] Revert "try to match against multiple templates" This reverts commit d017b95616a00b5fafd5f3b6a5b7fa2a6fc6c061. --- routes/virtual.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/routes/virtual.js b/routes/virtual.js index f8a27b33..b0c0d1f6 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -38,18 +38,12 @@ function registerRRPair(service, rrpair) { } else { debug("HTTP methods don't match"); - if(matched) - return next(); - else if( matched == false) - return next(new Error("HTTP methods does not match")); + return next(); } debug("Request matched? " + matched); // run the next callback if request not matched - if (!matched){ - debug("Error in request payload :: matched: " + matched); - return next(new Error("Error in Request payload")); - } + if (!matched) return next(); // function for matching requests to responses function matchRequest(payload) { @@ -268,4 +262,4 @@ module.exports = { deregisterById: deregisterById, deregisterService: deregisterService, registerAllRRPairsForAllServices: registerAllRRPairsForAllServices -}; +}; \ No newline at end of file From de7cafb8cca644f2cdea0b354011e2963a91ad09 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Thu, 1 Nov 2018 11:18:16 -0400 Subject: [PATCH 18/20] Fix bug in dynamically removing / editing routes --- lib/remove-route/index.js | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/remove-route/index.js b/lib/remove-route/index.js index b6f6ae49..e16ce0b0 100755 --- a/lib/remove-route/index.js +++ b/lib/remove-route/index.js @@ -16,7 +16,8 @@ function _findRoute(path,stack) { if (layer.name == 'router') { routes=routes.concat(_findRoute(trimPrefix(path, layer.path),layer.handle.stack)); } else { - if (layer.name == 'bound ') { + // change made per https://github.com/brennancheung/express-remove-route/pull/9 - JDW + if (layer.name == 'bound dispatch') { routes.push({route: layer || null, stack: stack}); } } @@ -48,11 +49,11 @@ module.exports = function removeRoute(app, path, method) { if (route) { if(_.isEmpty(method)){ // if no method delete all resource with the given path idx = stack.indexOf(route); - // https://github.com/brennancheung/express-remove-route/issues/3 + // change made to resolve https://github.com/brennancheung/express-remove-route/issues/3 - JDW if (idx>0) stack.splice(idx, 1); }else if(JSON.stringify(route.route.methods).toUpperCase().indexOf(method.toUpperCase())>=0){ // if method defined delete only the resource with the given ath and method idx = stack.indexOf(route); - // https://github.com/brennancheung/express-remove-route/issues/3 + // change made to resolve https://github.com/brennancheung/express-remove-route/issues/3 - JDW if (idx>0) stack.splice(idx, 1); } } diff --git a/package.json b/package.json index 0eab8ce2..e903c0fd 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "cookie-parser": "~1.4.3", "debug": "^2.6.9", "dotenv": "^5.0.1", - "express": "~4.13.4", + "express": "4.13.4", "express-actuator": "^1.1.0", "helmet": "^3.12.1", "jsonwebtoken": "^7.4.2", From e147aefdfa2356abe86447e72a60bd5ab2c27c59 Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Thu, 1 Nov 2018 14:32:24 -0400 Subject: [PATCH 19/20] Fix bugs in dynamically removing / editing routes --- controllers/serviceController.js | 6 +++++- lib/remove-route/index.js | 4 ++-- routes/virtual.js | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/controllers/serviceController.js b/controllers/serviceController.js index eaad6550..26c31f78 100644 --- a/controllers/serviceController.js +++ b/controllers/serviceController.js @@ -210,7 +210,11 @@ function updateService(req, res) { // don't let consumer alter name, base path, etc. service.matchTemplates = req.body.matchTemplates; service.rrpairs = req.body.rrpairs; - if (req.body.delay) service.delay = req.body.delay; + + const delay = req.body.delay; + if (delay || delay === 0) { + service.delay = req.body.delay; + } // save updated service in DB service.save(function (err, newService) { diff --git a/lib/remove-route/index.js b/lib/remove-route/index.js index e16ce0b0..b37cb369 100755 --- a/lib/remove-route/index.js +++ b/lib/remove-route/index.js @@ -50,11 +50,11 @@ module.exports = function removeRoute(app, path, method) { if(_.isEmpty(method)){ // if no method delete all resource with the given path idx = stack.indexOf(route); // change made to resolve https://github.com/brennancheung/express-remove-route/issues/3 - JDW - if (idx>0) stack.splice(idx, 1); + if (idx>=0) stack.splice(idx, 1); }else if(JSON.stringify(route.route.methods).toUpperCase().indexOf(method.toUpperCase())>=0){ // if method defined delete only the resource with the given ath and method idx = stack.indexOf(route); // change made to resolve https://github.com/brennancheung/express-remove-route/issues/3 - JDW - if (idx>0) stack.splice(idx, 1); + if (idx>=0) stack.splice(idx, 1); } } }); diff --git a/routes/virtual.js b/routes/virtual.js index b0c0d1f6..dfe8f07d 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -83,8 +83,8 @@ function registerRRPair(service, rrpair) { trimmedReqData[field] = flatReqData[field]; } - debug(JSON.stringify(trimmedPayload, null, 2)); - debug(JSON.stringify(trimmedReqData, null, 2)); + debug('received payload (from template): ' + JSON.stringify(trimmedPayload, null, 2)); + debug('expected payload (from template): ' + JSON.stringify(trimmedReqData, null, 2)); match = deepEquals(trimmedPayload, trimmedReqData); @@ -216,6 +216,7 @@ function registerById(id) { try { deregisterService(service); + debug('service running: ' + service.running); if (service.running) { service.rrpairs.forEach(function(rrpair){ registerRRPair(service, rrpair); From b8276da4ff4852b27cb62918f16c0fe6a0966dbc Mon Sep 17 00:00:00 2001 From: jweeks16 Date: Fri, 2 Nov 2018 10:19:43 -0400 Subject: [PATCH 20/20] fix match template issues for rest --- routes/virtual.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/routes/virtual.js b/routes/virtual.js index dfe8f07d..ed6180b9 100644 --- a/routes/virtual.js +++ b/routes/virtual.js @@ -68,9 +68,22 @@ function registerRRPair(service, rrpair) { for (let template of templates) { if (rrpair.payloadType === 'XML') { xml2js.parseString(template, function(err, xmlTemplate) { + if (err) { + debug(err); + return; + } template = xmlTemplate; }); } + else if (rrpair.payloadType === 'JSON') { + try { + template = JSON.parse(template); + } + catch(e) { + debug(e); + continue; + } + } const flatTemplate = flattenObject(template); const flatPayload = flattenObject(payload);