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..a21b5118 --- /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.name && 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) { + return 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..335a315f --- /dev/null +++ b/lib/merge.js @@ -0,0 +1,43 @@ +// 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 { + // capture case differences and recurse + lItems[match].name = item.name; + 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) { + 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..dc8adb69 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -88,3 +88,82 @@ 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 () { + + // swagger doc has an unnamed root that should not be used in the collapse. + var tree = { + item: [ + { + name:"level1", + item:[ + { + name:"level2", + item:[ + { + name:"level3", + item:[]}]}]}] + }, + result = Converter.collapse(tree); + + expect(result.result).to.equal(true, result.reason); + 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"); + }); +});