From 5f48d40ed1ee2a0825ee9a1103fb54e5f2c0538a Mon Sep 17 00:00:00 2001 From: Zackary Runner Date: Wed, 17 Oct 2018 01:16:32 -0700 Subject: [PATCH 1/3] Add Merge and Collapse functions --- index.js | 4 +++- lib/collapse.js | 38 ++++++++++++++++++++++++++++++++++++++ lib/convert.js | 17 +++++++++-------- lib/helpers.js | 10 +++++----- lib/merge.js | 41 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 lib/collapse.js create mode 100644 lib/merge.js diff --git a/index.js b/index.js index 69f2b266..275dfbba 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ // Exports the interface for the plugin module.exports = { validate: require('./lib/validate'), - convert: require('./lib/convert') + convert: require('./lib/convert'), + collapse: require('./lib/collapse'), + merge: require('./lib/merge') }; diff --git a/lib/collapse.js b/lib/collapse.js new file mode 100644 index 00000000..0adafaf0 --- /dev/null +++ b/lib/collapse.js @@ -0,0 +1,38 @@ +// Exports the collapse function for the plugin +/** +folder - a postman folder json node from which to start collapsing unnecessary folders +*/ +Compressor = { + collapse: function (folder) { + var prefix = folder.name, + firstPass = true, + result = { result: true, reason: "" }; + + if (!folder.item) { + return { result: false, reason: "folder contains no items" }; + } + + while (folder.item.length == 1 && !folder.request && !folder.item[0].request) { + folder.name = prefix + "/" + folder.item[0].name; + folder.item = folder.item[0].item; + + if (firstPass) { + prefix += "/..."; + firstPass = false; + } + } + + folder.item.forEach(item => { + var subResult = this.collapse(item); + if (subResult.result == false) { + result = subResult; + } + }); + + return result; + } +}; + +module.exports = function (folder) { + Compressor.collapse(folder); +} \ No newline at end of file diff --git a/lib/convert.js b/lib/convert.js index 5e51c4fe..66546e19 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -7,7 +7,7 @@ Converter = { // Static Props: collection: null, // will hold the V2 collection object - createCollectionStructure: function (swaggerData, tree) { + createCollectionStructure: function (swaggerData, tree, simpleCollapse) { // this takes in the tree structure, and creates the collection struct // with folders and params and requests @@ -15,7 +15,7 @@ Converter = { for (var child in tree.children) { if (tree.children.hasOwnProperty(child)) { this.collection.items.add( - Helpers.convertChildToItemGroup(swaggerData, tree.children[child]) + Helpers.convertChildToItemGroup(swaggerData, tree.children[child], simpleCollapse) ); } } @@ -23,16 +23,17 @@ Converter = { // takes in a swagger2 JSON object // returns a V2 collection JSON object - convert: function (json) { + convert: function (json, settings) { // No validation needed. If the app didn't call validate, this will throw an error var result = { status: true, - collecion: null, + collection: null, reason: null }, parseResult, swaggerData = {}, - tree; + tree, + simpleCollapse = !settings || settings.simpleCollapse; // maintain old default if (typeof json === 'string') { //parse @@ -65,7 +66,7 @@ Converter = { this.collection.variables = _.map(swaggerData.sampleDefinitions); tree = Helpers.getTreeFromPaths(json); - this.createCollectionStructure(swaggerData, tree); + this.createCollectionStructure(swaggerData, tree, simpleCollapse); result.collection = this.collection.toJSON(); @@ -74,6 +75,6 @@ Converter = { }; // Exports the convert function for the plugin -module.exports = function (json) { - return Converter.convert(json); +module.exports = function (json, settings) { + return Converter.convert(json, settings); }; diff --git a/lib/helpers.js b/lib/helpers.js index c61c70f9..8df50350 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -74,15 +74,15 @@ module.exports = { return retVal; }, - convertChildToItemGroup: function(swaggerData, child) { + convertChildToItemGroup: function(swaggerData, child, simpleCollapse) { var thisItemGroup, thisItem, rCount, subChild, i, oneRequest; // child will be a folder or request // depending on the type if (child.type === 'group') { // folder - if (child.requestCount > 1) { - // Folder with more than 1 request + if (child.requestCount > 1 || !simpleCollapse) { + // Folder with more than 1 request or if simple collapse is off // Should be converted to a folder thisItemGroup = new ItemGroup({ name: child.name @@ -90,12 +90,12 @@ module.exports = { for (subChild in child.children) { if (child.children.hasOwnProperty(subChild)) { thisItemGroup.items.add( - this.convertChildToItemGroup(swaggerData, child.children[subChild]) + this.convertChildToItemGroup(swaggerData, child.children[subChild], simpleCollapse) ); } } for (i = 0, rCount = child.requests.length; i < rCount; i++) { - thisItemGroup.items.add(this.convertChildToItemGroup(swaggerData, child.requests[i])); + thisItemGroup.items.add(this.convertChildToItemGroup(swaggerData, child.requests[i], simpleCollapse)); } return thisItemGroup; diff --git a/lib/merge.js b/lib/merge.js new file mode 100644 index 00000000..c4c14975 --- /dev/null +++ b/lib/merge.js @@ -0,0 +1,41 @@ +// Exports the merge function for the plugin, merges two Postman v2 nodes +/** +left - a node from the left document +right - a node from the right document to merge into and override the left +*/ +Merger = { + merge: function (left, right) { + var lItems = left["item"]; + var rItems = right["item"]; + + if (lItems == null || rItems == null) { + return {result:false, reason:"No 'item' member in left or right tree"}; + } + + var lKeys = []; + lItems.forEach(item => {lKeys.push(item["name"].toUpperCase()) }); + + rItems.forEach(item => { + var match = lKeys.indexOf(item["name"].toUpperCase()); + if (match >= 0) { + // If a folder, recurse. If an endpoint that already exists, replace. + if (item.request) { + lItems[match] = item; + } + else { + this.merge(lItems[match], item); + } + } + else { + // not found in the left items already, add it + lItems.push(item); + } + }); + + return {result:true} + } +} + +module.exports = function (left, right) { + Merger.merge(left, right); +}; \ No newline at end of file From d22e9d3a9066f8ed9445f5e3cc01ce7cf76cbc88 Mon Sep 17 00:00:00 2001 From: Zackary Runner Date: Thu, 18 Oct 2018 11:48:00 -0700 Subject: [PATCH 2/3] add tests, fix returns --- lib/collapse.js | 2 +- lib/merge.js | 6 ++-- test/unit/base.test.js | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/lib/collapse.js b/lib/collapse.js index 0adafaf0..876c5a48 100644 --- a/lib/collapse.js +++ b/lib/collapse.js @@ -34,5 +34,5 @@ Compressor = { }; module.exports = function (folder) { - Compressor.collapse(folder); + return Compressor.collapse(folder); } \ No newline at end of file diff --git a/lib/merge.js b/lib/merge.js index c4c14975..335a315f 100644 --- a/lib/merge.js +++ b/lib/merge.js @@ -23,6 +23,8 @@ Merger = { lItems[match] = item; } else { + // capture case differences and recurse + lItems[match].name = item.name; this.merge(lItems[match], item); } } @@ -32,10 +34,10 @@ Merger = { } }); - return {result:true} + return {result:true}; } } module.exports = function (left, right) { - Merger.merge(left, right); + return Merger.merge(left, right); }; \ No newline at end of file diff --git a/test/unit/base.test.js b/test/unit/base.test.js index 32ddb74f..14047847 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -88,3 +88,77 @@ describe('The converter must convert a swagger object', function() { expect(result.collection).to.have.property('item'); }); }); + +describe('Merge - ', function () { + it('Two different nodes', function () { + var left = { + item: [{name:"item1", item:[{name:"inner1"}]}] + }, + right = { + item: [{name:"Item2", item:[{name:"inner2"}]}] + }, + result = Converter.merge(left, right); + + expect(result.result).to.equal(true); + expect(left.item.length).to.equal(2); + expect(left.item[0].name).to.equal("item1"); + expect(left.item[1].name).to.equal("Item2"); + expect(left.item[0].item[0].name).to.equal("inner1"); + expect(left.item[1].item[0].name).to.equal("inner2"); + }); + + it('Right request overrides left', function () { + var left = { + item: [{name:"item1", item:[{name:"inner1", request:"nonNull"}]}] + }, + right = { + item: [{name:"Item1", item:[{name:"inner1", request:"nonNull2"}]}] + }, + result = Converter.merge(left, right); + + expect(result.result).to.equal(true); + expect(left.item.length).to.equal(1); + expect(left.item[0].name).to.equal("Item1"); + expect(left.item[0].item[0].name).to.equal("inner1"); + expect(left.item[0].item[0].request).to.equal("nonNull2"); + }); +}); + + +describe('Compress - ', function () { + it('With requests is uncompressed', function () { + var tree = { + name: "root", + item: [ + { + name:"level1", + request:"request", + item:[ + { + name:"level2", + item:[]}]}] + }, + result = Converter.collapse(tree); + + expect(result.result).to.equal(true, result.reason); + expect(tree.item[0].item[0].name).to.equal("level2"); + }); + + it('Empty folders are compressed', function () { + var tree = { + name: "root", + item: [ + { + name:"level1", + item:[ + { + name:"level2", + item:[]}]}] + }, + result = Converter.collapse(tree); + + expect(result.result).to.equal(true, result.reason); + expect(tree.item.length).to.equal(0); + expect(tree.name).to.equal("root/.../level2"); + }); +}); From 0a209264db201e07a0421936494bc7686f3f7aa4 Mon Sep 17 00:00:00 2001 From: Zackary Runner Date: Thu, 18 Oct 2018 21:57:44 -0700 Subject: [PATCH 3/3] don't collapse the root node --- lib/collapse.js | 2 +- test/unit/base.test.js | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/collapse.js b/lib/collapse.js index 876c5a48..a21b5118 100644 --- a/lib/collapse.js +++ b/lib/collapse.js @@ -12,7 +12,7 @@ Compressor = { return { result: false, reason: "folder contains no items" }; } - while (folder.item.length == 1 && !folder.request && !folder.item[0].request) { + while (folder.name && folder.item.length == 1 && !folder.request && !folder.item[0].request) { folder.name = prefix + "/" + folder.item[0].name; folder.item = folder.item[0].item; diff --git a/test/unit/base.test.js b/test/unit/base.test.js index 14047847..dc8adb69 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -145,20 +145,25 @@ describe('Compress - ', function () { }); it('Empty folders are compressed', function () { + + // swagger doc has an unnamed root that should not be used in the collapse. var tree = { - name: "root", item: [ { name:"level1", item:[ { name:"level2", - item:[]}]}] + item:[ + { + name:"level3", + item:[]}]}]}] }, result = Converter.collapse(tree); expect(result.result).to.equal(true, result.reason); - expect(tree.item.length).to.equal(0); - expect(tree.name).to.equal("root/.../level2"); + expect(tree.item.length).to.equal(1); + expect(tree.item[0].item.length).to.equal(0); + expect(tree.item[0].name).to.equal("level1/.../level3"); }); });