diff --git a/.eslintrc b/.eslintrc index 989cacbda..bdd86c6c8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,6 +13,7 @@ "node": true, "es6": true }, + "parser": "babel-eslint", "rules": { // Possible Errors "comma-dangle": ["error", "never"], @@ -74,7 +75,7 @@ "no-implicit-coercion": "error", "no-implicit-globals": "error", "no-implied-eval": "error", - "no-invalid-this": "error", + "no-invalid-this": "off", "no-iterator": "error", "no-labels": "error", "no-lone-blocks": "error", diff --git a/index.js b/index.js index 7894a4096..d24b2a577 100644 --- a/index.js +++ b/index.js @@ -4,11 +4,11 @@ const SchemaPack = require('./lib/schemapack.js').SchemaPack; module.exports = { // Old API wrapping the new API - convert: function(input, options, cb) { + convert: async function(input, options, cb) { var schema = new SchemaPack(input, options); if (schema.validated) { - return schema.convert(cb); + return await schema.convert(cb); } return cb(null, schema.validationResult); }, diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index e203f1d35..0f861d2d0 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -7,6 +7,7 @@ const async = require('async'), sdk = require('postman-collection'), schemaFaker = require('../assets/json-schema-faker.js'), parse = require('./parse.js'), + fetch = require('isomorphic-fetch'), deref = require('./deref.js'), _ = require('lodash'), xmlFaker = require('./xmlSchemaFaker.js'), @@ -686,7 +687,7 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @returns {void} - generatedStore is modified in-place */ - addCollectionItemsUsingPaths: function (spec, generatedStore, components, options, schemaCache) { + addCollectionItemsUsingPaths: async function (spec, generatedStore, components, options, schemaCache) { var folderTree, folderObj, child, @@ -727,7 +728,7 @@ module.exports = { // requestCount is a property added to each node (folder/request) while constructing the trie if (folderTree.root.children.hasOwnProperty(child) && folderTree.root.children[child].requestCount > 0) { generatedStore.collection.items.add( - this.convertChildToItemGroup(spec, folderTree.root.children[child], + await this.convertChildToItemGroup(spec, folderTree.root.children[child], components, options, schemaCache, variableStore) ); } @@ -755,12 +756,18 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @returns {object} returns an object containing objects of tags and their requests */ - addCollectionItemsUsingTags: function(spec, generatedStore, components, options, schemaCache) { + addCollectionItemsUsingTags: async function(spec, generatedStore, components, options, schemaCache) { var globalTags = spec.tags || [], paths = spec.paths || {}, pathMethods, variableStore = {}, - tagFolders = {}; + tagFolders = {}, + commonParams = [], + collectionVariables, + pathLevelServers = '', + summary, + operationItem, + localTags; // adding globalTags in the tagFolder object that are defined at the root level _.forEach(globalTags, (globalTag) => { @@ -770,47 +777,51 @@ module.exports = { }; }); - _.forEach(paths, (currentPathObject, path) => { - var commonParams = [], - collectionVariables, - pathLevelServers = ''; + paths = Object.keys(paths).map((key) => { + return { ...paths[key], 'path': key }; + }); + + for (let i = 0; i < paths.length; i++) { + + commonParams = []; + collectionVariables; + pathLevelServers = ''; // discard the leading slash, if it exists - if (path[0] === '/') { - path = path.substring(1); + if (paths[i].path[0] === '/') { + paths[i].path = paths[i].path.substring(1); } // extracting common parameters for all the methods in the current path item - if (currentPathObject.hasOwnProperty('parameters')) { - commonParams = currentPathObject.parameters; + if (paths[i].hasOwnProperty('parameters')) { + commonParams = paths[i].parameters; } // storing common path/collection vars from the server object at the path item level - if (currentPathObject.hasOwnProperty('servers')) { - pathLevelServers = currentPathObject.servers; + if (paths[i].hasOwnProperty('servers')) { + pathLevelServers = paths[i].servers; // add path level server object's URL as collection variable collectionVariables = this.convertToPmCollectionVariables( pathLevelServers[0].variables, // these are path variables in the server block - this.fixPathVariableName(path), // the name of the variable + this.fixPathVariableName(paths[i].path), // the name of the variable this.fixPathVariablesInUrl(pathLevelServers[0].url) ); _.forEach(collectionVariables, (collectionVariable) => { generatedStore.collection.variables.add(collectionVariable); }); - delete currentPathObject.servers; + delete paths[i].servers; } // get available method names for this path (path item object can have keys apart from operations) - pathMethods = _.filter(_.keys(currentPathObject), (key) => { + pathMethods = _.filter(_.keys(paths[i]), (key) => { return _.includes(METHODS, key); }); - _.forEach(pathMethods, (pathMethod) => { - var summary, - operationItem = currentPathObject[pathMethod], - localTags = operationItem.tags; + for (let j = 0; j < pathMethods.length; j++) { + operationItem = paths[i][pathMethods[j]]; + localTags = operationItem.tags; // params - these contain path/header/body params operationItem.parameters = this.getRequestParams(operationItem.parameters, commonParams, @@ -822,17 +833,19 @@ module.exports = { if (_.isEmpty(localTags)) { let tempRequest = { name: summary, - method: pathMethod, - path: path, + method: pathMethods[j], + path: paths[i].path, properties: operationItem, type: 'item', servers: pathLevelServers || undefined }; - generatedStore.collection.items.add(this.convertRequestToItem( + generatedStore.collection.items.add(await this.convertRequestToItem( + // eslint-disable-next-line no-loop-func spec, tempRequest, components, options, schemaCache, variableStore)); } else { + // eslint-disable-next-line no-loop-func _.forEach(localTags, (localTag) => { // add undefined tag object with empty description if (!_.has(tagFolders, localTag)) { @@ -844,32 +857,39 @@ module.exports = { tagFolders[localTag].requests.push({ name: summary, - method: pathMethod, - path: path, + method: pathMethods[j], + path: paths[i].path, properties: operationItem, type: 'item', servers: pathLevelServers || undefined }); }); } - }); - }); - + } + } // Add all folders created from tags and corresponding operations // Iterate from bottom to top order to maintain tag order in spec - _.forEachRight(tagFolders, (tagFolder, tagName) => { + + tagFolders = Object.keys(tagFolders).map((key) => { + return { ...tagFolders[key], 'tagName': key }; + }); + tagFolders.reverse(); + for (let i = 0; i < tagFolders.length; i++) { + // eslint-disable-next-line one-var var itemGroup = new sdk.ItemGroup({ - name: tagName, - description: tagFolder.description + name: tagFolders[i].tagName, + description: tagFolders[i].description }); - _.forEach(tagFolder.requests, (request) => { - itemGroup.items.add(this.convertRequestToItem(spec, request, components, options, schemaCache, variableStore)); - }); + for (let j = 0; j < tagFolders[i].requests.length; j++) { + itemGroup.items.add(await this.convertRequestToItem( + spec, tagFolders[i].requests[j], components, options, schemaCache, variableStore + )); + } // Add folders first (before requests) in generated collection generatedStore.collection.items.prepend(itemGroup); - }); + } // variableStore contains all the kinds of variable created. // Add only the variables with type 'collection' to generatedStore.collection.variables @@ -946,7 +966,7 @@ module.exports = { * @returns {*} Postman itemGroup or request * @no-unit-test */ - convertChildToItemGroup: function (openapi, child, components, options, schemaCache, variableStore) { + convertChildToItemGroup: async function (openapi, child, components, options, schemaCache, variableStore) { options = _.merge({}, defaultOptions, options); var resource = child, @@ -974,14 +994,18 @@ module.exports = { resourceSubChild = resource.children[subChild]; resourceSubChild.name = resource.name + '/' + resourceSubChild.name; - return this.convertChildToItemGroup(openapi, resourceSubChild, components, options, schemaCache, variableStore); + return await this.convertChildToItemGroup( + openapi, resourceSubChild, components, options, schemaCache, variableStore + ); } /* eslint-enable */ // recurse over child leaf nodes // and add as children to this folder for (i = 0, requestCount = resource.requests.length; i < requestCount; i++) { itemGroup.items.add( - this.convertRequestToItem(openapi, resource.requests[i], components, options, schemaCache, variableStore) + await this.convertRequestToItem( + openapi, resource.requests[i], components, options, schemaCache, variableStore + ) ); } @@ -991,7 +1015,7 @@ module.exports = { for (subChild in resource.children) { if (resource.children.hasOwnProperty(subChild) && resource.children[subChild].requestCount > 0) { itemGroup.items.add( - this.convertChildToItemGroup(openapi, resource.children[subChild], components, options, schemaCache, + await this.convertChildToItemGroup(openapi, resource.children[subChild], components, options, schemaCache, variableStore) ); } @@ -1003,15 +1027,18 @@ module.exports = { // 2. it has only 1 direct request of its own if (resource.requests.length === 1) { - return this.convertRequestToItem(openapi, resource.requests[0], components, options, schemaCache, variableStore); + return await this.convertRequestToItem( + openapi, resource.requests[0], components, options, schemaCache, variableStore + ); } // 3. it's a folder that has no child request // but one request somewhere in its child folders for (subChild in resource.children) { if (resource.children.hasOwnProperty(subChild) && resource.children[subChild].requestCount === 1) { - return this.convertChildToItemGroup(openapi, resource.children[subChild], components, options, schemaCache, - variableStore); + return await this.convertChildToItemGroup( + openapi, resource.children[subChild], components, options, schemaCache, variableStore + ); } } }, @@ -1222,7 +1249,7 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @return {object} responseBody, contentType header needed */ - convertToPmResponseBody: function(contentObj, components, options, schemaCache) { + convertToPmResponseBody: async function(contentObj, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); var responseBody, cTypeHeader, hasComputedType, cTypes; @@ -1260,7 +1287,7 @@ module.exports = { }; } } - responseBody = this.convertToPmBodyData(contentObj[cTypeHeader], REQUEST_TYPE.EXAMPLE, cTypeHeader, + responseBody = await this.convertToPmBodyData(contentObj[cTypeHeader], REQUEST_TYPE.EXAMPLE, cTypeHeader, PARAMETER_SOURCE.RESPONSE, options.indentCharacter, components, options, schemaCache); if (this.getHeaderFamily(cTypeHeader) === HEADER_TYPE.JSON) { responseBody = JSON.stringify(responseBody, null, options.indentCharacter); @@ -1310,7 +1337,7 @@ module.exports = { * @param {*} exampleObj map[string, exampleObject] * @returns {*} first example in the input map type */ - getExampleData: function(exampleObj, components, options) { + getExampleData: async function(exampleObj, components, options) { options = _.merge({}, defaultOptions, options); var example, @@ -1328,10 +1355,15 @@ module.exports = { example = this.getRefObject(example.$ref, components, options); } - if (example.hasOwnProperty('value')) { + else if (example.hasOwnProperty('value')) { example = example.value; } + else if (example.hasOwnProperty('externalValue')) { + let data = await fetch(example.externalValue); + example = await data.json(); + } + return example; }, @@ -1352,7 +1384,7 @@ module.exports = { // and generate the body accordingly // right now, even if the content-type was XML, we'll generate // a JSON example/schema - convertToPmBodyData: function(bodyObj, requestType, contentType, parameterSourceOption, + convertToPmBodyData: async function(bodyObj, requestType, contentType, parameterSourceOption, indentCharacter, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); @@ -1384,7 +1416,7 @@ module.exports = { } else if (!_.isEmpty(bodyObj.examples) && (resolveTo === 'example' || !bodyObj.schema)) { // take one of the examples as the body and not all - bodyData = this.getExampleData(bodyObj.examples, components, options); + bodyData = await this.getExampleData(bodyObj.examples, components, options); } else if (bodyObj.schema) { if (bodyObj.schema.hasOwnProperty('$ref')) { @@ -1655,7 +1687,7 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @returns {Object} - Postman requestBody and Content-Type Header */ - convertToPmBody: function(requestBody, requestType, components, options, schemaCache) { + convertToPmBody: async function(requestBody, requestType, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); var contentObj, // content is required bodyData, @@ -1691,7 +1723,7 @@ module.exports = { if (contentObj[URLENCODED].hasOwnProperty('schema') && contentObj[URLENCODED].schema.hasOwnProperty('$ref')) { contentObj[URLENCODED].schema = this.getRefObject(contentObj[URLENCODED].schema.$ref, components, options); } - bodyData = this.convertToPmBodyData(contentObj[URLENCODED], requestType, URLENCODED, + bodyData = await this.convertToPmBodyData(contentObj[URLENCODED], requestType, URLENCODED, PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); encoding = contentObj[URLENCODED].encoding ? contentObj[URLENCODED].encoding : {}; // create query parameters and add it to the request body object @@ -1750,7 +1782,7 @@ module.exports = { } else if (contentObj.hasOwnProperty(FORM_DATA)) { rDataMode = 'formdata'; - bodyData = this.convertToPmBodyData(contentObj[FORM_DATA], requestType, FORM_DATA, + bodyData = await this.convertToPmBodyData(contentObj[FORM_DATA], requestType, FORM_DATA, PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); encoding = contentObj[FORM_DATA].encoding ? contentObj[FORM_DATA].encoding : {}; // create the form parameters and add it to the request body object @@ -1832,7 +1864,7 @@ module.exports = { } } - bodyData = this.convertToPmBodyData(contentObj[bodyType], requestType, bodyType, + bodyData = await this.convertToPmBodyData(contentObj[bodyType], requestType, bodyType, PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); updateOptions = { @@ -1865,7 +1897,7 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @returns {Object} postman response */ - convertToPmResponse: function(response, code, originalRequest, components, options, schemaCache) { + convertToPmResponse: async function(response, code, originalRequest, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); var responseHeaders = [], previewLanguage = 'text', @@ -1895,7 +1927,7 @@ module.exports = { } }); - responseBodyWrapper = this.convertToPmResponseBody(response.content, components, options, schemaCache); + responseBodyWrapper = await this.convertToPmResponseBody(response.content, components, options, schemaCache); if (responseBodyWrapper.contentTypeHeader) { // we could infer the content-type header from the body @@ -2096,7 +2128,7 @@ module.exports = { * @returns {Object} postman request Item * @no-unit-test */ - convertRequestToItem: function(openapi, operationItem, components, options, schemaCache, variableStore) { + convertRequestToItem: async function(openapi, operationItem, components, options, schemaCache, variableStore) { options = _.merge({}, defaultOptions, options); var reqName, pathVariables = openapi.baseUrlVariables, @@ -2313,7 +2345,7 @@ module.exports = { if (reqBody.$ref) { reqBody = this.getRefObject(reqBody.$ref, components, options); } - pmBody = this.convertToPmBody(reqBody, REQUEST_TYPE.ROOT, components, options, schemaCache); + pmBody = await this.convertToPmBody(reqBody, REQUEST_TYPE.ROOT, components, options, schemaCache); item.request.body = pmBody.body; item.request.addHeader(pmBody.contentHeader); // extra form headers if encoding is present in request Body. @@ -2344,14 +2376,18 @@ module.exports = { }); } - _.forOwn(operation.responses, (response, code) => { + responses = Object.keys(operation.responses).map((key) => { + return { ...operation.responses[key], 'code': key }; + }); + + for (let i = 0; i < responses.length; i++) { let originalRequestHeaders = [], originalRequestQueryParams = this.convertToPmQueryArray(reqParams, REQUEST_TYPE.EXAMPLE, components, options, schemaCache); - swagResponse = response; - if (response.$ref) { - swagResponse = this.getRefObject(response.$ref, components, options); + swagResponse = responses[i]; + if (responses[i].$ref) { + swagResponse = this.getRefObject(responses[i].$ref, components, options); } if (options.includeAuthInfoInExample) { @@ -2380,7 +2416,7 @@ module.exports = { thisOriginalRequest.header = originalRequestHeaders; // setting request body try { - exampleRequestBody = this.convertToPmBody(operationItem.properties.requestBody, + exampleRequestBody = await this.convertToPmBody(operationItem.properties.requestBody, REQUEST_TYPE.EXAMPLE, components, options, schemaCache); thisOriginalRequest.body = exampleRequestBody.body ? exampleRequestBody.body.toJSON() : {}; } @@ -2389,12 +2425,11 @@ module.exports = { // 'Exception:', e); thisOriginalRequest.body = {}; } - convertedResponse = this.convertToPmResponse(swagResponse, code, thisOriginalRequest, + convertedResponse = await this.convertToPmResponse(swagResponse, responses[i].code, thisOriginalRequest, components, options, schemaCache); convertedResponse && item.responses.add(convertedResponse); - }); + } } - return item; }, @@ -4325,11 +4360,12 @@ module.exports = { * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches * @returns {Array} - Array of all MISSING_ENDPOINT objects */ - getMissingSchemaEndpoints: function (schema, matchedEndpoints, components, options, schemaCache) { + getMissingSchemaEndpoints: async function (schema, matchedEndpoints, components, options, schemaCache) { let endpoints = [], schemaPaths = schema.paths, rootCollectionVariables, - schemaJsonPath; + schemaJsonPath, + pathKeys; // collection variables generated for resolving for baseUrl and variables rootCollectionVariables = this.convertToPmCollectionVariables( @@ -4338,39 +4374,45 @@ module.exports = { schema.baseUrl ); - _.forEach(schemaPaths, (schemaPathObj, schemaPath) => { - _.forEach(_.keys(schemaPathObj), (pathKey) => { - schemaJsonPath = `$.paths[${schemaPath}].${_.toLower(pathKey)}`; - if (METHODS.includes(pathKey) && !matchedEndpoints.includes(schemaJsonPath)) { + schemaPaths = Object.keys(schemaPaths).map((key) => { + return { ...schemaPaths[key], 'schemaPath': key }; + }); + + for (let i = 0; i < schemaPaths.length; i++) { + pathKeys = Object.keys(schemaPaths[i]); + for (let j = 0; j < pathKeys.length; j++) { + schemaJsonPath = `$.paths[${schemaPaths[i].schemaPath}].${_.toLower(pathKeys[j])}`; + if (METHODS.includes(pathKeys[j]) && !matchedEndpoints.includes(schemaJsonPath)) { let mismatchObj = { property: 'ENDPOINT', transactionJsonPath: null, schemaJsonPath, reasonCode: 'MISSING_ENDPOINT', - reason: `The endpoint "${_.toUpper(pathKey)} ${schemaPath}" is missing in collection`, - endpoint: _.toUpper(pathKey) + ' ' + schemaPath + reason: `The endpoint "${_.toUpper(pathKeys[j])} ${schemaPaths[i].schemaPath}" is missing in collection`, + endpoint: _.toUpper(pathKeys[j]) + ' ' + schemaPaths[i].schemaPath }; if (options.suggestAvailableFixes) { - let operationItem = _.get(schemaPathObj, pathKey, {}), + let operationItem = _.get(schemaPaths[i], pathKeys[j], {}), convertedRequest, variables = rootCollectionVariables, - path = schemaPath, + path = schemaPaths[i].schemaPath, request; // add common parameters of path level operationItem.parameters = this.getRequestParams(operationItem.parameters, - _.get(schemaPathObj, 'parameters'), components, options); + _.get(schemaPaths[i], 'parameters'), components, options); // discard the leading slash, if it exists + /* eslint-disable max-depth */ if (path[0] === '/') { path = path.substring(1); } // override root level collection variables (baseUrl and vars) with path level server url and vars if exists // storing common path/collection vars from the server object at the path item level - if (!_.isEmpty(_.get(schemaPathObj, 'servers'))) { - let pathLevelServers = schemaPathObj.servers; + if (!_.isEmpty(_.get(schemaPaths[i], 'servers'))) { + let pathLevelServers = schemaPaths[i].servers; // add path level server object's URL as collection variable variables = this.convertToPmCollectionVariables( @@ -4379,21 +4421,23 @@ module.exports = { this.fixPathVariablesInUrl(pathLevelServers[0].url) ); } - + /* eslint-enable */ request = { name: operationItem.summary || operationItem.description, - method: pathKey, - path: schemaPath[0] === '/' ? schemaPath.substring(1) : schemaPath, + method: pathKeys[j], + // eslint-disable-next-line max-len + path: schemaPaths[i].schemaPath[0] === '/' ? schemaPaths[i].schemaPath.substring(1) : schemaPaths[i].schemaPath, properties: operationItem, type: 'item', - servers: _.isEmpty(_.get(schemaPathObj, 'servers')) + servers: _.isEmpty(_.get(schemaPaths[i], 'servers')) }; // convert request to collection item and store collection variables - convertedRequest = this.convertRequestToItem(schema, request, components, options, schemaCache, variables); + // eslint-disable-next-line max-len + convertedRequest = await this.convertRequestToItem(schema, request, components, options, schemaCache, variables); mismatchObj.suggestedFix = { - key: pathKey, + key: pathKeys[j], actualValue: null, // Not adding colloection variables for now suggestedValue: { @@ -4405,8 +4449,8 @@ module.exports = { endpoints.push(mismatchObj); } - }); - }); + } + } return endpoints; } }; diff --git a/lib/schemapack.js b/lib/schemapack.js index c97581868..89223e702 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -221,7 +221,7 @@ class SchemaPack { // convert method, this is called when you want to convert a schema that you've already loaded // in the constructor - convert (callback) { + async convert (callback) { let openapi, options = this.computedOptions, analysis, @@ -299,10 +299,14 @@ class SchemaPack { // For paths, All operations are grouped based on corresponding paths try { if (options.folderStrategy === 'tags') { - schemaUtils.addCollectionItemsUsingTags(openapi, generatedStore, componentsAndPaths, options, schemaCache); + await schemaUtils.addCollectionItemsUsingTags( + openapi, generatedStore, componentsAndPaths, options, schemaCache + ); } else { - schemaUtils.addCollectionItemsUsingPaths(openapi, generatedStore, componentsAndPaths, options, schemaCache); + await schemaUtils.addCollectionItemsUsingPaths( + openapi, generatedStore, componentsAndPaths, options, schemaCache + ); } } catch (e) { @@ -333,7 +337,7 @@ class SchemaPack { * @param {*} callback return * @returns {boolean} validation */ - validateTransaction(transactions, callback) { + async validateTransaction (transactions, callback) { let schema = this.openapi, componentsAndPaths, analysis, @@ -536,13 +540,16 @@ class SchemaPack { } }); - retVal = { - requests: _.keyBy(result, 'requestId'), - missingEndpoints: schemaUtils.getMissingSchemaEndpoints(schema, matchedEndpoints, - componentsAndPaths, options, schemaCache) - }; - - callback(null, retVal); + schemaUtils.getMissingSchemaEndpoints(schema, matchedEndpoints, + componentsAndPaths, options, schemaCache).then((resp) => { + retVal = { + requests: _.keyBy(result, 'requestId'), + missingEndpoints: resp + }; + callback(null, retVal); + }).catch((e) => { + callback(e); + }); }); }, 0); } diff --git a/package-lock.json b/package-lock.json index f31da7f85..a7f0425a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -271,6 +271,20 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -982,6 +996,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -1031,6 +1051,15 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1176,6 +1205,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1220,6 +1258,15 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "requires": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, "istanbul-lib-coverage": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", @@ -1631,6 +1678,11 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "node-fetch-h2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", @@ -2268,6 +2320,16 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2725,6 +2787,11 @@ "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", "integrity": "sha1-9j/+2iSL8opnqNSODjtGGhZluvg=" }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index b482cca8a..de1ee6b31 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "ajv": "6.12.3", "async": "3.2.0", "commander": "2.20.3", + "isomorphic-fetch": "3.0.0", "js-yaml": "3.13.1", "json-schema-merge-allof": "0.7.0", "lodash": "4.17.21", @@ -130,6 +131,7 @@ "author": "Postman Labs ", "license": "Apache-2.0", "devDependencies": { + "babel-eslint": "10.1.0", "chai": "4.2.0", "editorconfig": "0.15.3", "eslint": "5.16.0", @@ -137,8 +139,8 @@ "eslint-plugin-mocha": "5.3.0", "eslint-plugin-security": "1.4.0", "expect.js": "0.3.1", - "nyc": "14.1.1", "mocha": "5.2.0", + "nyc": "14.1.1", "parse-gitignore": "0.5.1" }, "scripts": { diff --git a/test/unit/plugin.test.js b/test/unit/plugin.test.js index a0c35c079..546b8c05f 100644 --- a/test/unit/plugin.test.js +++ b/test/unit/plugin.test.js @@ -1,6 +1,6 @@ var path = '../../', expect = require('expect.js'), - package = require(path), + pkg = require(path), packageJson = require(path + '/package.json'); /* global describe, it */ @@ -17,18 +17,18 @@ describe('Plugin ' + packageJson.name, function() { }); it('should expose the required functions', function (done) { - expect(typeof package.validate).to.equal('function'); - expect(typeof package.convert).to.equal('function'); + expect(typeof pkg.validate).to.equal('function'); + expect(typeof pkg.convert).to.equal('function'); done(); }); it('should validate the sample input correctly', function (done) { - expect(package.validate(sampleInput).result).to.equal(true); + expect(pkg.validate(sampleInput).result).to.equal(true); done(); }); it('should convert the sample input correctly', function (done) { - package.convert(sampleInput, {}, function(err, result) { + pkg.convert(sampleInput, {}, function(err, result) { expect(err).to.be(null); expect(result.result).to.equal(true); result.output.forEach(function (element) { diff --git a/test/unit/util.test.js b/test/unit/util.test.js index 7c11546b2..0f55a38f1 100644 --- a/test/unit/util.test.js +++ b/test/unit/util.test.js @@ -646,30 +646,30 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { }); describe('convertToPmBodyData', function() { - it('should work for schemas', function() { + it('should work for schemas', async function() { var bodyWithSchema = { schema: { type: 'integer', format: 'int32' } }, - retValSchema = SchemaUtils.convertToPmBodyData(bodyWithSchema, 'ROOT', 'application/json'); + retValSchema = await SchemaUtils.convertToPmBodyData(bodyWithSchema, 'ROOT', 'application/json'); expect(retValSchema).to.be.equal(''); }); - it('should work for example', function() { + it('should work for example', async function() { var bodyWithExample = { example: { value: 'This is a sample value' } }, - retValExample = SchemaUtils.convertToPmBodyData(bodyWithExample, 'application/json'); + retValExample = await SchemaUtils.convertToPmBodyData(bodyWithExample, 'application/json'); expect(retValExample).to.equal('This is a sample value'); }); - it('should work for examples', function() { + it('should work for examples', async function() { var bodyWithExamples = { examples: { foo: { @@ -680,19 +680,19 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } } }, - retValExamples = SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'application/json', + retValExamples = await SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'application/json', 'request', ' ', null, { requestParametersResolution: 'example' }); expect(retValExamples.foo).to.equal(1); expect(retValExamples.bar).to.equal(2); }); - it('should work for examples with a $ref for non-json requests', function() { + it('should work for examples with a $ref for non-json requests', async function() { var bodyWithExamples = { 'example': { '$ref': '#/components/examples/SampleExample/value' } }, - retValExample = SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'text/plain', + retValExample = await SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'text/plain', 'request', ' ', { components: { examples: { @@ -708,13 +708,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { expect(retValExample).to.equal('Hello'); }); - it('should work for examples with a $ref for json requests', function() { + it('should work for examples with a $ref for json requests', async function() { var bodyWithExamples = { 'example': { '$ref': '#/components/examples/SampleExample/value' } }, - retValExample = SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'application/json', + retValExample = await SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'application/json', 'request', ' ', { 'components': { 'examples': { @@ -1508,7 +1508,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { describe('convertToPmBody function', function() { describe('should convert requestbody of media type', function() { - it(' application/json', function(done) { + it(' application/json', async function() { var requestBody = { description: 'body description', content: { @@ -1537,14 +1537,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = JSON.parse(result.body.raw); expect(resultBody.id).to.equal(''); expect(resultBody.name).to.equal(''); expect(result.contentHeader).to.deep.include({ key: 'Content-Type', value: 'application/json' }); - done(); }); - it(' application/x-www-form-urlencoded', function(done) { + it(' application/x-www-form-urlencoded', async function() { var requestBody = { description: 'body description', content: { @@ -1554,14 +1553,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = (result.body.urlencoded.toJSON()); expect(resultBody).to.eql([]); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'application/x-www-form-urlencoded' }); - done(); }); - it(' multipart/form-data', function(done) { + it(' multipart/form-data', async function() { var requestBody = { description: 'body description', content: { @@ -1582,14 +1580,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = (result.body.formdata.toJSON()); expect(resultBody[0].key).to.equal('file'); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'multipart/form-data' }); - done(); }); - it(' text/xml', function(done) { // not properly done + it(' text/xml', async function() { // not properly done var requestBody = { description: 'body description', content: { @@ -1604,16 +1601,15 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody, 'ROOT', {}, { + result = await SchemaUtils.convertToPmBody(requestBody, 'ROOT', {}, { requestParametersResolution: 'example' }); resultBody = (result.body.raw); expect(resultBody).to.equal('"text/plain description"'); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'text/xml' }); - done(); }); - it(' text/plain', function(done) { + it(' text/plain', async function() { var requestBody = { description: 'body description', content: { @@ -1626,14 +1622,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = result.body.raw; expect(resultBody).to.equal('"text/plain description"'); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'text/plain' }); - done(); }); - it(' text/html', function(done) { + it(' text/html', async function() { var requestBody = { description: 'body description', content: { @@ -1646,14 +1641,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = (result.body.raw); expect(resultBody).to.equal('"
  • item 1
  • item 2
"'); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'text/html' }); - done(); }); - it(' application/javascript', function(done) { // not properly done + it(' application/javascript', async function() { // not properly done var requestBody = { description: 'body description', content: { @@ -1662,12 +1656,11 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, result, resultBody; - result = SchemaUtils.convertToPmBody(requestBody); + result = await SchemaUtils.convertToPmBody(requestBody); resultBody = (result.body.raw); expect(typeof resultBody).to.equal('string'); expect(result.contentHeader).to.deep.include( { key: 'Content-Type', value: 'application/javascript' }); - done(); }); // things remaining : application/xml }); @@ -1675,13 +1668,14 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { describe('convertToPmResponseBody function', function() { describe('should convert content object to response body data', function() { - it('with undefined ContentObj', function() { + it('with undefined ContentObj', async function() { var contentObj, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = pmResponseBody.responseBody; expect(pmResponseBody).to.equal(''); }); - it('with Content-Type application/json', function() { + it('with Content-Type application/json', async function() { var contentObj = { 'application/json': { 'schema': { @@ -1703,11 +1697,12 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = JSON.parse(pmResponseBody.responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); - it('with Content-Type application/vnd.retailer.v2+json', function() { + it('with Content-Type application/vnd.retailer.v2+json', async function() { var contentObj = { 'application/vnd.retailer.v2+json': { 'schema': { @@ -1729,11 +1724,12 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = JSON.parse(pmResponseBody.responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); - it('with Content-Type application/vnd.api+json', function() { + it('with Content-Type application/vnd.api+json', async function() { var contentObj = { 'application/vnd.api+json': { 'schema': { @@ -1755,11 +1751,12 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = JSON.parse(pmResponseBody.responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); - it('with Content-Type application/json and specified indentCharacter', function() { + it('with Content-Type application/json and specified indentCharacter', async function() { var contentObj = { 'application/json': { 'schema': { @@ -1773,12 +1770,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj, {}, { + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj, {}, { indentCharacter: '\t' - }).responseBody; + }); + pmResponseBody = pmResponseBody.responseBody; expect(pmResponseBody).to.equal('{\n\t"id": ""\n}'); }); - it('with Content-Type text/plain', function() { + it('with Content-Type text/plain', async function() { var contentObj = { 'text/plain': { 'schema': { @@ -1787,10 +1785,11 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = pmResponseBody.responseBody; expect(typeof pmResponseBody).to.equal('string'); }); - it('with Content-Type application/xml', function() { + it('with Content-Type application/xml', async function() { var contentObj = { 'application/xml': { 'schema': { @@ -1813,9 +1812,10 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj, {}, { + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj, {}, { indentCharacter: ' ' - }).responseBody; + }); + pmResponseBody = pmResponseBody.responseBody; expect(pmResponseBody).to.equal( [ '', @@ -1829,16 +1829,17 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { '' ].join('\n')); }); - it('with Content-Type application/javascript', function() { + it('with Content-Type application/javascript', async function() { var contentObj = { 'application/javascript': { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = pmResponseBody.responseBody; expect(typeof pmResponseBody).to.equal('string'); }); - it('with Content-Type unsupported', function() { + it('with Content-Type unsupported', async function() { var contentObj = { 'application/vnd.api+json+unsupported': { 'schema': { @@ -1860,7 +1861,8 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = await SchemaUtils.convertToPmResponseBody(contentObj); + pmResponseBody = pmResponseBody.responseBody; expect(pmResponseBody).to.equal(''); }); // things remaining application/xml, application/javascript @@ -1868,7 +1870,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { }); describe('convertToPmResponse function', function() { - it('should convert response with JSON content field', function(done) { + it('should convert response with JSON content field', async function() { var response = { 'description': 'A list of pets.', 'content': { @@ -1895,7 +1897,8 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { code = '20X', pmResponse, responseBody; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = await SchemaUtils.convertToPmResponse(response, code); + pmResponse = pmResponse.toJSON(); responseBody = JSON.parse(pmResponse.body); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(200); @@ -1906,9 +1909,8 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { }); expect(responseBody.id).to.equal(''); expect(responseBody.name).to.equal(''); - done(); }); - it('should convert response with XML content field', function(done) { + it('should convert response with XML content field', async function() { var response = { 'description': 'A list of pets.', 'content': { @@ -1935,7 +1937,8 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { code = '20X', pmResponse; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = await SchemaUtils.convertToPmResponse(response, code); + pmResponse = pmResponse.toJSON(); expect(pmResponse.body).to.equal('\n (integer)\n (string)\n'); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(200); @@ -1944,16 +1947,16 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { 'key': 'Content-Type', 'value': 'application/xml' }); - done(); }); - it('should convert response without content field', function(done) { + it('should convert response without content field', async function() { var response = { 'description': 'A list of pets.' }, code = '201', pmResponse; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = await SchemaUtils.convertToPmResponse(response, code); + pmResponse = pmResponse.toJSON(); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(201); expect(pmResponse.body).to.equal(''); @@ -1961,9 +1964,8 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { 'key': 'Content-Type', 'value': 'text/plain' }); - done(); }); - it('should convert headers with refs', function(done) { + it('should convert headers with refs', async function() { var response = { 'description': '`Too Many Requests`\\n', 'headers': { @@ -1973,7 +1975,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, code = '200', - pmResponse = SchemaUtils.convertToPmResponse(response, code, null, { + pmResponse = await SchemaUtils.convertToPmResponse(response, code, null, { components: { 'responses': { 'TooManyRequests': { @@ -2006,7 +2008,6 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { expect(pmResponse.headers.members[0].key).to.equal('Retry-After'); expect(pmResponse.headers.members[0].description).to.equal('Some description'); - done(); }); });