diff --git a/lib/common/versionUtils.js b/lib/common/versionUtils.js index 5ac3291..ca3276b 100644 --- a/lib/common/versionUtils.js +++ b/lib/common/versionUtils.js @@ -123,6 +123,9 @@ function getConcreteSchemaUtils({ type, data }) { if (specVersion === DEFAULT_SPEC_VERSION) { concreteUtils = require('../30XUtils/schemaUtils30X'); } + else if (specVersion === VERSION_20.version) { + concreteUtils = require('../swaggerUtils/schemaUtilsSwagger'); + } else { concreteUtils = require('../31XUtils/schemaUtils31X'); } @@ -142,8 +145,22 @@ function filterOptionsByVersion(options, version) { return options; } +/** + * Calculates if thew current input is using swagger 2.0 spec + * @param {string} version The current spec version + * @returns {boolean} True if the current spec is using swagger 2.0 spec + */ +function isSwagger(version) { + let isSwagger = false; + if (version === VERSION_20.version) { + isSwagger = true; + } + return isSwagger; +} + module.exports = { getSpecVersion, getConcreteSchemaUtils, - filterOptionsByVersion + filterOptionsByVersion, + isSwagger }; diff --git a/lib/options.js b/lib/options.js index ac62c7a..d2d0898 100644 --- a/lib/options.js +++ b/lib/options.js @@ -4,7 +4,8 @@ const _ = require('lodash'), VALID_MODES = ['document', 'use'], VERSION30 = '3.0', VERSION31 = '3.1', - SUPPORTED_VERSIONS = [VERSION30, VERSION31]; + VERSION20 = '2.0', + SUPPORTED_VERSIONS = [VERSION20, VERSION30, VERSION31]; /** * Takes a list of arguments and resolve them acording its content @@ -66,7 +67,7 @@ module.exports = { ' values: `description`, `operationid`, `url`.', external: true, usage: ['CONVERSION', 'VALIDATION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Set indent character', @@ -77,7 +78,7 @@ module.exports = { description: 'Option for setting indentation character', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Collapse redundant folders', @@ -88,7 +89,7 @@ module.exports = { 'persistent folder-level data.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Optimize conversion', @@ -99,7 +100,7 @@ module.exports = { ' the performance of conversion.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Request parameter generation', @@ -113,7 +114,7 @@ module.exports = { ' in the schema.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Response parameter generation', @@ -127,7 +128,7 @@ module.exports = { ' in the schema.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Folder organization', @@ -138,7 +139,7 @@ module.exports = { description: 'Select whether to create folders according to the spec’s paths or tags.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Enable Schema Faking', @@ -148,7 +149,7 @@ module.exports = { description: 'Whether or not schemas should be faked.', external: false, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Schema resolution nesting limit', @@ -160,7 +161,7 @@ module.exports = { ' option works correctly "optimizeConversion" option needs to be disabled)', external: false, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Include auth info in example requests', @@ -170,7 +171,7 @@ module.exports = { description: 'Select whether to include authentication parameters in the example request', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Short error messages during request <> schema validation', @@ -276,7 +277,7 @@ module.exports = { description: 'Whether to set optional parameters as disabled', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Keep implicit headers', @@ -286,7 +287,7 @@ module.exports = { description: 'Whether to keep implicit headers from the OpenAPI specification, which are removed by default.', external: true, usage: ['CONVERSION'], - supportedIn: [VERSION30, VERSION31] + supportedIn: [VERSION20, VERSION30, VERSION31] }, { name: 'Include webhooks', diff --git a/lib/schemapack.js b/lib/schemapack.js index c4227a3..5619ff9 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -3,6 +3,7 @@ // This is the default collection name if one can't be inferred from the OpenAPI spec const COLLECTION_NAME = 'Imported from OpenAPI 3.0', { getConcreteSchemaUtils } = require('./common/versionUtils.js'), + { convertToOAS30IfSwagger } = require('./swaggerUtils/swaggerToOpenapi.js'), BROWSER = 'browser', Ajv = require('ajv'), addFormats = require('ajv-formats'), @@ -60,7 +61,10 @@ class SchemaPack { validate() { let input = this.input, json, - specParseResult; + specParseResult, + isFolder = this.input.type === 'folder'; + + this.computedOptions = Object.assign({ isFolder }, this.computedOptions); if (!input) { return { result: false, @@ -252,113 +256,121 @@ class SchemaPack { return callback(new OpenApiErr('The schema must be validated before attempting conversion')); } - // this cannot be attempted before validation - specComponentsAndUtils = { concreteUtils }; - Object.assign(specComponentsAndUtils, concreteUtils.getRequiredData(this.openapi)); - - // create and sanitize basic spec - openapi = this.openapi; - openapi.servers = _.isEmpty(openapi.servers) ? [{ url: '/' }] : openapi.servers; - openapi.securityDefs = _.get(openapi, 'components.securitySchemes', {}); - openapi.baseUrl = _.get(openapi, 'servers.0.url', '{{baseURL}}'); - - // TODO: Multiple server variables need to be saved as environments - openapi.baseUrlVariables = _.get(openapi, 'servers.0.variables'); - - // Fix {scheme} and {path} vars in the URL to :scheme and :path - openapi.baseUrl = schemaUtils.fixPathVariablesInUrl(openapi.baseUrl); - - // Creating a new instance of a Postman collection - // All generated folders and requests will go inside this - generatedStore.collection = new sdk.Collection({ - info: { - name: _.isEmpty(_.get(openapi, 'info.title')) ? COLLECTION_NAME : _.get(openapi, 'info.title') - } - }); - - if (openapi.security) { - authHelper = schemaUtils.getAuthHelper(openapi, openapi.security); - if (authHelper) { - generatedStore.collection.auth = authHelper; - } - } - // ---- Collection Variables ---- - // adding the collection variables for all the necessary root level variables - // and adding them to the collection variables - schemaUtils.convertToPmCollectionVariables( - openapi.baseUrlVariables, - 'baseUrl', - openapi.baseUrl - ).forEach((element) => { - generatedStore.collection.variables.add(element); - }); - - generatedStore.collection.describe(schemaUtils.getCollectionDescription(openapi)); - - // Only change the stack limit if the optimizeConversion option is true - if (options.optimizeConversion) { - // Deciding stack limit based on size of the schema, number of refs and number of paths. - analysis = schemaUtils.analyzeSpec(openapi); - - // Update options on the basis of analysis. - options = schemaUtils.determineOptions(analysis, options); - } - - - // ---- Collection Items ---- - // Adding the collection items from openapi spec based on folderStrategy option - // For tags, All operations are grouped based on respective tags object - // For paths, All operations are grouped based on corresponding paths - try { - if (options.folderStrategy === 'tags') { - schemaUtils.addCollectionItemsUsingTags( - openapi, - generatedStore, - specComponentsAndUtils, - options, - schemaCache, - concreteUtils - ); - } - else { - schemaUtils.addCollectionItemsUsingPaths( - openapi, - generatedStore, - specComponentsAndUtils, - options, - schemaCache, - concreteUtils - ); + // We only convert if swagger is found otherwise this.openapi remains the same + convertToOAS30IfSwagger(concreteUtils, this.openapi, (error, newOpenapi) => { + if (error) { + return callback(error); } - if (options.includeWebhooks) { - schemaUtils.addCollectionItemsFromWebhooks( - openapi, - generatedStore, - specComponentsAndUtils, - options, - schemaCache, - concreteUtils - ); + this.openapi = newOpenapi; + // this cannot be attempted before validation + specComponentsAndUtils = { concreteUtils }; + Object.assign(specComponentsAndUtils, concreteUtils.getRequiredData(this.openapi)); + + // create and sanitize basic spec + openapi = this.openapi; + openapi.servers = _.isEmpty(openapi.servers) ? [{ url: '/' }] : openapi.servers; + openapi.securityDefs = _.get(openapi, 'components.securitySchemes', {}); + openapi.baseUrl = _.get(openapi, 'servers.0.url', '{{baseURL}}'); + + // TODO: Multiple server variables need to be saved as environments + openapi.baseUrlVariables = _.get(openapi, 'servers.0.variables'); + + // Fix {scheme} and {path} vars in the URL to :scheme and :path + openapi.baseUrl = schemaUtils.fixPathVariablesInUrl(openapi.baseUrl); + + // Creating a new instance of a Postman collection + // All generated folders and requests will go inside this + generatedStore.collection = new sdk.Collection({ + info: { + name: _.isEmpty(_.get(openapi, 'info.title')) ? COLLECTION_NAME : _.get(openapi, 'info.title') + } + }); + + if (openapi.security) { + authHelper = schemaUtils.getAuthHelper(openapi, openapi.security); + if (authHelper) { + generatedStore.collection.auth = authHelper; + } } - } - catch (e) { - return callback(e); - } + // ---- Collection Variables ---- + // adding the collection variables for all the necessary root level variables + // and adding them to the collection variables + schemaUtils.convertToPmCollectionVariables( + openapi.baseUrlVariables, + 'baseUrl', + openapi.baseUrl + ).forEach((element) => { + generatedStore.collection.variables.add(element); + }); - collectionJSON = generatedStore.collection.toJSON(); + generatedStore.collection.describe(schemaUtils.getCollectionDescription(openapi)); - // this needs to be deleted as even if version is not specified to sdk, - // it returns a version property with value set as undefined - // this fails validation against v2.1 collection schema definition. - delete collectionJSON.info.version; + // Only change the stack limit if the optimizeConversion option is true + if (options.optimizeConversion) { + // Deciding stack limit based on size of the schema, number of refs and number of paths. + analysis = schemaUtils.analyzeSpec(openapi); - return callback(null, { - result: true, - output: [{ - type: 'collection', - data: collectionJSON - }] + // Update options on the basis of analysis. + options = schemaUtils.determineOptions(analysis, options); + } + + + // ---- Collection Items ---- + // Adding the collection items from openapi spec based on folderStrategy option + // For tags, All operations are grouped based on respective tags object + // For paths, All operations are grouped based on corresponding paths + try { + if (options.folderStrategy === 'tags') { + schemaUtils.addCollectionItemsUsingTags( + openapi, + generatedStore, + specComponentsAndUtils, + options, + schemaCache, + concreteUtils + ); + } + else { + schemaUtils.addCollectionItemsUsingPaths( + openapi, + generatedStore, + specComponentsAndUtils, + options, + schemaCache, + concreteUtils + ); + } + + if (options.includeWebhooks) { + schemaUtils.addCollectionItemsFromWebhooks( + openapi, + generatedStore, + specComponentsAndUtils, + options, + schemaCache, + concreteUtils + ); + } + } + catch (e) { + return callback(e); + } + + collectionJSON = generatedStore.collection.toJSON(); + + // this needs to be deleted as even if version is not specified to sdk, + // it returns a version property with value set as undefined + // this fails validation against v2.1 collection schema definition. + delete collectionJSON.info.version; + + return callback(null, { + result: true, + output: [{ + type: 'collection', + data: collectionJSON + }] + }); }); } diff --git a/lib/swaggerUtils/inputValidationSwagger.js b/lib/swaggerUtils/inputValidationSwagger.js new file mode 100644 index 0000000..753cbbe --- /dev/null +++ b/lib/swaggerUtils/inputValidationSwagger.js @@ -0,0 +1,42 @@ + +module.exports = { + + /** + * Validate Spec to check if some of the required fields are present. + * @param {Object} spec OpenAPI spec + * @param {object} options Validation options + * @return {Object} Validation result + */ + validateSpec: function (spec, options) { + if (spec.swagger !== '2.0') { + return { + result: false, + reason: 'The value of "swagger" field must be 2.0' + }; + } + if (!spec.info) { + return { + result: false, + reason: 'The Swagger specification must have an "info" field' + }; + } + if (!(spec.info.title && spec.info.version) && !options.isFolder) { + return { + result: false, + reason: 'Title, and version fields are required for the Info Object' + }; + } + if (!spec.paths) { + return { + result: false, + reason: 'The Swagger specification must have a "paths" field' + }; + } + + // Valid. No reason needed + return { + result: true, + openapi: spec + }; + } +}; diff --git a/lib/swaggerUtils/schemaUtilsSwagger.js b/lib/swaggerUtils/schemaUtilsSwagger.js new file mode 100644 index 0000000..2849de1 --- /dev/null +++ b/lib/swaggerUtils/schemaUtilsSwagger.js @@ -0,0 +1,74 @@ +const inputValidationSwagger = require('./inputValidationSwagger'), + schemaUtilsCommon = require('../common/schemaUtilsCommon'), + _ = require('lodash'); + + +module.exports = { + version: '2.0', + + /** + * Parses an OAS string/object as a YAML or JSON + * @param {YAML/JSON} openApiSpec - The swagger 2.0 specification specified in either YAML or JSON + * @param {Object} options computed process options + * @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error + * @no-unit-test + */ + parseSpec: function (openApiSpec, options) { + return schemaUtilsCommon.parseSpec(openApiSpec, inputValidationSwagger, options); + }, + + /** + * Get the required elements for conversion from spec parsed data + * @param {object} spec openapi parsed value + * @returns {object} required elements to convert + */ + getRequiredData: function(spec) { + return { + info: spec.info, + components: spec.components ? spec.components : [], + paths: spec.paths + }; + }, + + /** + * Compares two types and return if they match or not + * @param {string} currentType the type in schema + * @param {string} typeToValidate the type to compare + * @returns {boolean} the result of the comparation + */ + compareTypes(currentType, typeToValidate) { + return currentType === typeToValidate; + }, + + /** + * This method is to make this module matches with schemaUtilsXXX interface content + * It only returns the provided schema + * @param {object} schema a provided schema + * @returns {object} it returns the same schema + */ + fixExamplesByVersion(schema) { + return schema; + }, + + /** + * Check if request body type is binary type + * @param {string} bodyType the bodyType provided in a request body content + * @param {object} contentObj The request body content provided in spec + * @returns {boolean} Returns true if content is a binary type + */ + isBinaryContentType (bodyType, contentObj) { + return bodyType && + !_.isEmpty(_.get(contentObj, [bodyType, 'schema'])) && + contentObj[bodyType].schema.type === 'string' && + contentObj[bodyType].schema.format === 'binary'; + }, + + getOuterPropsIfIsSupported() { + return undefined; + }, + + addOuterPropsToRefSchemaIfIsSupported(refSchema) { + return refSchema; + }, + inputValidation: inputValidationSwagger +}; diff --git a/lib/swaggerUtils/swaggerToOpenapi.js b/lib/swaggerUtils/swaggerToOpenapi.js new file mode 100644 index 0000000..98d3e60 --- /dev/null +++ b/lib/swaggerUtils/swaggerToOpenapi.js @@ -0,0 +1,35 @@ +const Swagger2OpenAPI = require('swagger2openapi'), + { isSwagger } = require('../common/versionUtils'); + +module.exports = { + + /** + * Converts a Swagger 2.0 API definition into an OpenAPI 3.0 specification + * @param {Object} concreteUtils Concrete schema utils according to the specification version + * @param {object} parsedSwagger Parsed Swagger spec + * @param {function} convertExecution Function to perform the OAS-PM convertion after the Spec convertion + * @return {Object} {error, newOpenapi} The new open api spec or error if there was an error in the process + */ + convertToOAS30IfSwagger: function(concreteUtils, parsedSwagger, convertExecution) { + if (isSwagger(concreteUtils.version)) { + Swagger2OpenAPI.convertObj( + parsedSwagger, + { + fatal: false, + patch: true, + anchors: true, + warnOnly: true + }, + (error, newOpenapi) => { + if (error) { + return convertExecution(error); + } + return convertExecution(null, newOpenapi.openapi); + } + ); + } + else { + return convertExecution(null, parsedSwagger); + } + } +}; diff --git a/package-lock.json b/package-lock.json index d3d4f26..806cea7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -329,6 +329,11 @@ "to-fast-properties": "^2.0.0" } }, + "@exodus/schemasafe": { + "version": "1.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.0.0-rc.6.tgz", + "integrity": "sha512-dDnQizD94EdBwEj/fh3zPRa/HWCS9O5au2PuHhZBbuM3xWHxuaKzPBOEWze7Nn0xW68MIpZ7Xdyn1CoCpjKCuQ==" + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -413,10 +418,9 @@ } }, "ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -555,6 +559,11 @@ "get-intrinsic": "^1.0.2" } }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -674,12 +683,6 @@ "wrap-ansi": "^6.2.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -987,6 +990,11 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -1602,6 +1610,12 @@ "through": "^2.3.6" }, "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2138,6 +2152,12 @@ "yargs-unparser": "1.6.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -2361,6 +2381,14 @@ } } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-fetch-h2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", @@ -2378,6 +2406,14 @@ "process-on-spawn": "^1.0.0" } }, + "node-readfiles": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", + "integrity": "sha1-271K8SE04uY1wkXvk//Pb2BnOl0=", + "requires": { + "es6-promise": "^3.2.1" + } + }, "node-releases": { "version": "1.1.71", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz", @@ -2464,6 +2500,16 @@ "fast-safe-stringify": "^2.0.7" } }, + "oas-linter": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", + "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", + "requires": { + "@exodus/schemasafe": "^1.0.0-rc.2", + "should": "^13.2.1", + "yaml": "^1.10.0" + } + }, "oas-resolver-browser": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/oas-resolver-browser/-/oas-resolver-browser-2.5.6.tgz", @@ -2477,11 +2523,6 @@ "yargs": "^17.0.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2557,6 +2598,128 @@ } } }, + "oas-schema-walker": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", + "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==" + }, + "oas-validator": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", + "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", + "requires": { + "call-me-maybe": "^1.0.1", + "oas-kit-common": "^1.0.8", + "oas-linter": "^3.2.2", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "reftools": "^1.1.9", + "should": "^13.2.1", + "yaml": "^1.10.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "requires": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + } + }, + "reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + } + } + }, "object-inspect": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", @@ -2987,6 +3150,54 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "should": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", + "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "requires": { + "should-equal": "^2.0.0", + "should-format": "^3.0.3", + "should-type": "^1.4.0", + "should-type-adaptors": "^1.0.1", + "should-util": "^1.0.0" + } + }, + "should-equal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", + "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", + "requires": { + "should-type": "^1.4.0" + } + }, + "should-format": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", + "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", + "requires": { + "should-type": "^1.3.0", + "should-type-adaptors": "^1.0.1" + } + }, + "should-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", + "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=" + }, + "should-type-adaptors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", + "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", + "requires": { + "should-type": "^1.3.0", + "should-util": "^1.0.0" + } + }, + "should-util": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", + "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==" + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -3073,11 +3284,6 @@ "strip-ansi": "^6.0.1" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3118,9 +3324,9 @@ }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true } } @@ -3146,6 +3352,126 @@ "has-flag": "^3.0.0" } }, + "swagger2openapi": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", + "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", + "requires": { + "call-me-maybe": "^1.0.1", + "node-fetch": "^2.6.1", + "node-fetch-h2": "^2.3.0", + "node-readfiles": "^0.2.0", + "oas-kit-common": "^1.0.8", + "oas-resolver": "^2.5.6", + "oas-schema-walker": "^1.1.5", + "oas-validator": "^5.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "oas-resolver": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", + "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", + "requires": { + "node-fetch-h2": "^2.3.0", + "oas-kit-common": "^1.0.8", + "reftools": "^1.1.9", + "yaml": "^1.10.0", + "yargs": "^17.0.1" + } + }, + "reftools": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", + "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yargs": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==" + } + } + }, "table": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/table/-/table-5.4.5.tgz", @@ -3170,6 +3496,12 @@ "uri-js": "^4.2.2" } }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -3263,6 +3595,11 @@ "is-number": "^7.0.0" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -3363,6 +3700,20 @@ "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", "integrity": "sha1-9j/+2iSL8opnqNSODjtGGhZluvg=" }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -3435,12 +3786,6 @@ "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3539,12 +3884,6 @@ "yargs-parser": "^18.1.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", @@ -3588,6 +3927,12 @@ "yargs": "^13.3.0" }, "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", diff --git a/package.json b/package.json index 4af3844..097657f 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "oas-resolver-browser": "2.5.6", "path-browserify": "1.0.1", "postman-collection": "4.0.0", + "swagger2openapi": "7.0.8", "yaml": "1.10.2" }, "author": "Postman Labs ", diff --git a/test/data/invalid_swagger/invalid_no_info.json b/test/data/invalid_swagger/invalid_no_info.json new file mode 100644 index 0000000..7f249ed --- /dev/null +++ b/test/data/invalid_swagger/invalid_no_info.json @@ -0,0 +1,1042 @@ +{ + "swagger": "2.0", + "not_info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma seperated strings", + "operationId": "findPetsByStatus", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "consumes": [ + "application/xml", + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": false, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when toekn expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key_param", + "in": "query" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/invalid_swagger/invalid_no_info_title.json b/test/data/invalid_swagger/invalid_no_info_title.json new file mode 100644 index 0000000..8e4c27c --- /dev/null +++ b/test/data/invalid_swagger/invalid_no_info_title.json @@ -0,0 +1,1042 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "not_title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma seperated strings", + "operationId": "findPetsByStatus", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "consumes": [ + "application/xml", + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": false, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when toekn expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key_param", + "in": "query" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/invalid_swagger/invalid_no_paths.json b/test/data/invalid_swagger/invalid_no_paths.json new file mode 100644 index 0000000..2842d8b --- /dev/null +++ b/test/data/invalid_swagger/invalid_no_paths.json @@ -0,0 +1,231 @@ +{ + "swagger": "2.0", + "info": { + "title": "Swagger test", + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "not_title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key_param", + "in": "query" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/invalid_swagger/invalid_wrong_swagger_version.json b/test/data/invalid_swagger/invalid_wrong_swagger_version.json new file mode 100644 index 0000000..7a10314 --- /dev/null +++ b/test/data/invalid_swagger/invalid_wrong_swagger_version.json @@ -0,0 +1,1042 @@ +{ + "swagger": "3.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma seperated strings", + "operationId": "findPetsByStatus", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "consumes": [ + "application/xml", + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": false, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when toekn expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key_param", + "in": "query" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/swaggerMultifile/basicExample/index.yaml b/test/data/swaggerMultifile/basicExample/index.yaml new file mode 100644 index 0000000..f2f08fa --- /dev/null +++ b/test/data/swaggerMultifile/basicExample/index.yaml @@ -0,0 +1,9 @@ +swagger: "2.0" +info: + $ref: ./info.yaml +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + $ref: ./paths.yaml \ No newline at end of file diff --git a/test/data/swaggerMultifile/basicExample/info.yaml b/test/data/swaggerMultifile/basicExample/info.yaml new file mode 100644 index 0000000..19565f9 --- /dev/null +++ b/test/data/swaggerMultifile/basicExample/info.yaml @@ -0,0 +1,3 @@ +title: Sample API +description: API description in Markdown. +version: 1.0.0 diff --git a/test/data/swaggerMultifile/basicExample/paths.yaml b/test/data/swaggerMultifile/basicExample/paths.yaml new file mode 100644 index 0000000..ab1b648 --- /dev/null +++ b/test/data/swaggerMultifile/basicExample/paths.yaml @@ -0,0 +1,9 @@ +/users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/definitions/User.yaml b/test/data/swaggerMultifile/multifile-two-root-files/definitions/User.yaml new file mode 100644 index 0000000..d1f7947 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/definitions/User.yaml @@ -0,0 +1,4 @@ +type: object +properties: + name: + type: string \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/definitions/index.yaml b/test/data/swaggerMultifile/multifile-two-root-files/definitions/index.yaml new file mode 100644 index 0000000..ad27c60 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/definitions/index.yaml @@ -0,0 +1,2 @@ +User: + $ref: ./User.yaml \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/index.yaml b/test/data/swaggerMultifile/multifile-two-root-files/index.yaml new file mode 100644 index 0000000..d19d181 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/index.yaml @@ -0,0 +1,7 @@ +swagger: '2.0' +info: + $ref: ./info/index.yaml +paths: + $ref: ./paths/index.yaml +definitions: + $ref: ./definitions/index.yaml diff --git a/test/data/swaggerMultifile/multifile-two-root-files/index1.yaml b/test/data/swaggerMultifile/multifile-two-root-files/index1.yaml new file mode 100644 index 0000000..32120a0 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/index1.yaml @@ -0,0 +1,7 @@ +swagger: '2.0' +info: + $ref: ./info/index1.yaml +paths: + $ref: ./paths/index.yaml +definitions: + $ref: ./definitions/index.yaml diff --git a/test/data/swaggerMultifile/multifile-two-root-files/info/index.yaml b/test/data/swaggerMultifile/multifile-two-root-files/info/index.yaml new file mode 100644 index 0000000..a0ef082 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/info/index.yaml @@ -0,0 +1,2 @@ +version: 0.0.0 +title: Simple API \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/info/index1.yaml b/test/data/swaggerMultifile/multifile-two-root-files/info/index1.yaml new file mode 100644 index 0000000..a0ef082 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/info/index1.yaml @@ -0,0 +1,2 @@ +version: 0.0.0 +title: Simple API \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/paths/bar.yaml b/test/data/swaggerMultifile/multifile-two-root-files/paths/bar.yaml new file mode 100644 index 0000000..d700039 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/paths/bar.yaml @@ -0,0 +1,8 @@ +get: + responses: + 200: + description: OK + schema: + $ref: '../definitions/User.yaml' + required: + - name \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/paths/foo.yaml b/test/data/swaggerMultifile/multifile-two-root-files/paths/foo.yaml new file mode 100644 index 0000000..c20f618 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/paths/foo.yaml @@ -0,0 +1,4 @@ +get: + responses: + 200: + description: OK \ No newline at end of file diff --git a/test/data/swaggerMultifile/multifile-two-root-files/paths/index.yaml b/test/data/swaggerMultifile/multifile-two-root-files/paths/index.yaml new file mode 100644 index 0000000..51ffc75 --- /dev/null +++ b/test/data/swaggerMultifile/multifile-two-root-files/paths/index.yaml @@ -0,0 +1,4 @@ +/foo: + $ref: ./foo.yaml +/bar: + $ref: ./bar.yaml \ No newline at end of file diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/common/Error.yaml b/test/data/swaggerMultifile/petstore-separate-yaml/common/Error.yaml new file mode 100644 index 0000000..fe25636 --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/common/Error.yaml @@ -0,0 +1,10 @@ +type: object +required: + - code + - message +properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/spec/NewPet.yaml b/test/data/swaggerMultifile/petstore-separate-yaml/spec/NewPet.yaml new file mode 100644 index 0000000..531e8df --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/spec/NewPet.yaml @@ -0,0 +1,9 @@ +type: object +allOf: + - $ref: 'Pet.yaml' + - required: + - name + properties: + description: + type: integer + format: int64 \ No newline at end of file diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/spec/Pet.yaml b/test/data/swaggerMultifile/petstore-separate-yaml/spec/Pet.yaml new file mode 100644 index 0000000..4e69ee1 --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/spec/Pet.yaml @@ -0,0 +1,12 @@ +type: object +required: + - id + - name +properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string \ No newline at end of file diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/spec/parameters.yaml b/test/data/swaggerMultifile/petstore-separate-yaml/spec/parameters.yaml new file mode 100644 index 0000000..a95e16b --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/spec/parameters.yaml @@ -0,0 +1,16 @@ +tagsParam: + name: tags + in: query + description: tags to filter by + required: false + type: array + collectionFormat: csv + items: + type: string +limitsParam: + name: limit + in: query + description: maximum number of results to return + required: false + type: integer + format: int32 \ No newline at end of file diff --git a/test/data/swaggerMultifile/petstore-separate-yaml/spec/swagger.yaml b/test/data/swaggerMultifile/petstore-separate-yaml/spec/swagger.yaml new file mode 100644 index 0000000..87cb295 --- /dev/null +++ b/test/data/swaggerMultifile/petstore-separate-yaml/spec/swagger.yaml @@ -0,0 +1,100 @@ +swagger: "2.0" +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification + termsOfService: http://swagger.io/terms/ + contact: + name: Swagger API Team + email: apiteam@swagger.io + url: http://swagger.io + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html +host: petstore.swagger.io +basePath: /api +schemes: + - http +consumes: + - application/json +produces: + - application/json +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + parameters: + - $ref: 'parameters.yaml#/tagsParam' + - $ref: 'parameters.yaml#/limitsParam' + responses: + "200": + description: pet response + schema: + type: array + items: + $ref: 'Pet.yaml' + default: + description: unexpected error + schema: + $ref: '../common/Error.yaml' + post: + description: Creates a new pet in the store. Duplicates are allowed + operationId: addPet + parameters: + - name: pet + in: body + description: Pet to add to the store + required: true + schema: + $ref: 'NewPet.yaml' + responses: + "200": + description: pet response + schema: + $ref: 'Pet.yaml' + default: + description: unexpected error + schema: + $ref: '../common/Error.yaml' + /pets/{id}: + get: + description: Returns a user based on a single ID, if the user does not have access to the pet + operationId: find pet by id + parameters: + - name: id + in: path + description: ID of pet to fetch + required: true + type: integer + format: int64 + responses: + "200": + description: pet response + schema: + $ref: 'Pet.yaml' + default: + description: unexpected error + schema: + $ref: '../common/Error.yaml' + delete: + description: deletes a single pet based on the ID supplied + operationId: deletePet + parameters: + - name: id + in: path + description: ID of pet to delete + required: true + type: integer + format: int64 + responses: + "204": + description: pet deleted + default: + description: unexpected error + schema: + $ref: '../common/Error.yaml' \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/Activities.yaml b/test/data/swaggerMultifile/uberTest/definitions/Activities.yaml new file mode 100644 index 0000000..a74cfe8 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/Activities.yaml @@ -0,0 +1,17 @@ +properties: + offset: + type: integer + format: int32 + description: Position in pagination. + limit: + type: integer + format: int32 + description: Number of items to retrieve (100 max). + count: + type: integer + format: int32 + description: Total number of items available. + history: + type: array + items: + $ref: '#/definitions/Activity' \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/Activity.yaml b/test/data/swaggerMultifile/uberTest/definitions/Activity.yaml new file mode 100644 index 0000000..d6a8ec9 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/Activity.yaml @@ -0,0 +1,4 @@ +properties: + uuid: + type: string + description: Unique identifier for the activity \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/Error.yaml b/test/data/swaggerMultifile/uberTest/definitions/Error.yaml new file mode 100644 index 0000000..b805f17 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/Error.yaml @@ -0,0 +1,8 @@ +properties: + code: + type: integer + format: int32 + message: + type: string + fields: + type: string \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/PriceEstimate.yaml b/test/data/swaggerMultifile/uberTest/definitions/PriceEstimate.yaml new file mode 100644 index 0000000..3c589cb --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/PriceEstimate.yaml @@ -0,0 +1,22 @@ +properties: + product_id: + type: string + description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles + currency_code: + type: string + description: "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code." + display_name: + type: string + description: Display name of product. + estimate: + type: string + description: Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or "Metered" for TAXI. + low_estimate: + type: number + description: Lower bound of the estimated price. + high_estimate: + type: number + description: Upper bound of the estimated price. + surge_multiplier: + type: number + description: Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier. \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/Product.yaml b/test/data/swaggerMultifile/uberTest/definitions/Product.yaml new file mode 100644 index 0000000..5f52a04 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/Product.yaml @@ -0,0 +1,16 @@ +properties: + product_id: + type: string + description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles. + description: + type: string + description: Description of product. + display_name: + type: string + description: Display name of product. + capacity: + type: integer + description: Capacity of product. For example, 4 people. + image: + type: string + description: Image URL representing the product. \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/ProductList.yaml b/test/data/swaggerMultifile/uberTest/definitions/ProductList.yaml new file mode 100644 index 0000000..8c4df55 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/ProductList.yaml @@ -0,0 +1,6 @@ +properties: + products: + description: Contains the list of products + type: array + items: + $ref: "#/definitions/Product" \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/definitions/Profile.yaml b/test/data/swaggerMultifile/uberTest/definitions/Profile.yaml new file mode 100644 index 0000000..6493e19 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/definitions/Profile.yaml @@ -0,0 +1,16 @@ +properties: + first_name: + type: string + description: First name of the Uber user. + last_name: + type: string + description: Last name of the Uber user. + email: + type: string + description: Email address of the Uber user + picture: + type: string + description: Image URL of the Uber user. + promo_code: + type: string + description: Promo code of the Uber user. \ No newline at end of file diff --git a/test/data/swaggerMultifile/uberTest/index.yaml b/test/data/swaggerMultifile/uberTest/index.yaml new file mode 100644 index 0000000..be4cff9 --- /dev/null +++ b/test/data/swaggerMultifile/uberTest/index.yaml @@ -0,0 +1,186 @@ +swagger: "2.0" +info: + title: Uber API + description: Move your app forward with the Uber API + version: "1.0.0" +host: api.uber.com +schemes: + - https +basePath: /v1 +securityDefinitions: + apikey: + type: apiKey + name: server_token + in: query +produces: + - application/json +paths: + /products: + get: + summary: Product Types + description: The Products endpoint returns information about the Uber products offered at a given location. The response includes the display name and other details about each product, and lists the products in the proper display order. + parameters: + - name: latitude + in: query + description: Latitude component of location. + required: true + type: number + format: double + - name: longitude + in: query + description: Longitude component of location. + required: true + type: number + format: double + security: + - apikey: [] + tags: + - Products + responses: + "200": + description: An array of products + schema: + type: array + items: + $ref: '#/definitions/Product' + default: + description: Unexpected error + schema: + $ref: '#/definitions/Error' + /estimates/price: + get: + summary: Price Estimates + description: The Price Estimates endpoint returns an estimated price range for each product offered at a given location. The price estimate is provided as a formatted string with the full price range and the localized currency symbol.

The response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for situations requiring currency conversion. When surge is active for a particular product, its surge_multiplier will be greater than 1, but the price estimate already factors in this multiplier. + parameters: + - name: start_latitude + in: query + description: Latitude component of start location. + required: true + type: number + format: double + - name: start_longitude + in: query + description: Longitude component of start location. + required: true + type: number + format: double + - name: end_latitude + in: query + description: Latitude component of end location. + required: true + type: number + format: double + - name: end_longitude + in: query + description: Longitude component of end location. + required: true + type: number + format: double + tags: + - Estimates + responses: + "200": + description: An array of price estimates by product + schema: + type: array + items: + $ref: '#/definitions/PriceEstimate' + default: + description: Unexpected error + schema: + $ref: '#/definitions/Error' + /estimates/time: + get: + summary: Time Estimates + description: The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs. + parameters: + - name: start_latitude + in: query + description: Latitude component of start location. + required: true + type: number + format: double + - name: start_longitude + in: query + description: Longitude component of start location. + required: true + type: number + format: double + - name: customer_uuid + in: query + type: string + format: uuid + description: Unique customer identifier to be used for experience customization. + - name: product_id + in: query + type: string + description: Unique identifier representing a specific product for a given latitude & longitude. + tags: + - Estimates + responses: + "200": + description: An array of products + schema: + type: array + items: + $ref: '#/definitions/Product' + default: + description: Unexpected error + schema: + $ref: '#/definitions/Error' + /me: + get: + summary: User Profile + description: The User Profile endpoint returns information about the Uber user that has authorized with the application. + tags: + - User + responses: + "200": + description: Profile information for a user + schema: + $ref: '#/definitions/Profile' + default: + description: Unexpected error + schema: + $ref: '#/definitions/Error' + /history: + get: + summary: User Activity + description: The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.

The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary. + parameters: + - name: offset + in: query + type: integer + format: int32 + description: Offset the list of returned results by this amount. Default is zero. + - name: limit + in: query + type: integer + format: int32 + description: Number of items to retrieve. Default is 5, maximum is 100. + tags: + - User + responses: + "200": + description: History information for the given user + schema: + $ref: '#/definitions/Activities' + default: + description: Unexpected error + schema: + $ref: '#/definitions/Error' +definitions: + Product: + $ref: ./definitions/Product.yaml + ProductList: + $ref: ./definitions/ProductList.yaml + PriceEstimate: + $ref: ./definitions/PriceEstimate.yaml + Profile: + $ref: ./definitions/Profile.yaml + Activity: + $ref: ./definitions/Activity.yaml + Activities: + $ref: ./definitions/Activities.yaml + Error: + $ref: ./definitions/Error.yaml \ No newline at end of file diff --git a/test/data/valid_swagger/json/petstorePatchable.json b/test/data/valid_swagger/json/petstorePatchable.json new file mode 100644 index 0000000..fa4fdea --- /dev/null +++ b/test/data/valid_swagger/json/petstorePatchable.json @@ -0,0 +1,151 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v1", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "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 + } + ], + "responses": { + "200": { + "description": "An paged array of pets", + "headers": { + "x-next": { + "type": "string", + "description": "A link to the next page of responses" + } + }, + "schema": { + "$ref": "#/definitions/Pets" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "tags": [ + "pets" + ], + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/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", + "type": "string" + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "schema": { + "$ref": "#/definitions/Pets" + } + }, + "default": { + "description": "unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Pet": { + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + }, + "Error": { + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/sampleswagger.json b/test/data/valid_swagger/json/sampleswagger.json new file mode 100644 index 0000000..ef2db1d --- /dev/null +++ b/test/data/valid_swagger/json/sampleswagger.json @@ -0,0 +1,1042 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": false, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma seperated strings", + "operationId": "findPetsByStatus", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "consumes": [ + "application/xml", + "application/json" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "consumes": [ + "application/xml", + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": false, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "string" + }, + "headers": { + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + }, + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when toekn expires" + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": false, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key_param", + "in": "query" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger 2.json b/test/data/valid_swagger/json/swagger 2.json new file mode 100644 index 0000000..9a07564 --- /dev/null +++ b/test/data/valid_swagger/json/swagger 2.json @@ -0,0 +1,54 @@ +{ + "swagger": "2.0", + "info": { + "description": "My API", + "version": "1.0.0", + "title": "My API", + "termsOfService": "http://www.domain.com", + "contact": { + "name": "support@domain.com" + } + }, + "basePath": "/", + "schemes": [ + "http" + ], + "paths": { + "Authorization/LoginAPI": { + "post": { + "summary": "Authenticates you to the system and produces a session token that will be used for future calls", + "description": "", + "operationId": "LoginAPI", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "parameters": [{ + "in": "formData", + "name": "UserName", + "description": "Login Username", + "required": true, + "type": "string", + "default": "Zaphod" + }, { + "in": "formData", + "name": "Password", + "description": "Password", + "required": true, + "type": "string" + + }], + "responses": { + "200": { + "description": "API Response with session ID if login is allowed", + "schema": { + "$ref": "#/definitions/Authorization" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger-with-path.json b/test/data/valid_swagger/json/swagger-with-path.json new file mode 100644 index 0000000..adf2a79 --- /dev/null +++ b/test/data/valid_swagger/json/swagger-with-path.json @@ -0,0 +1,379 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters", + "version": "1.0.0", + "title": "Swagger Petstore", + "termsOfService": "http://helloreverb.com/terms/", + "contact": { + "email": "apiteam@wordnik.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "http" + ], + "paths": { + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "consumes": [ + "application/xml", + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "string" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/xml", + "application/json" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + } + }, + "securityDefinitions": { + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "http://petstore.swagger.io/api/oauth/dialog", + "flow": "implicit", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + }, + "definitions": { + "Order": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + }, + "Category": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Tag": { + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "name": "photoUrl", + "wrapped": true + }, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "xml": { + "name": "tag", + "wrapped": true + }, + "items": { + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "ApiResponse": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger2-with-params.json b/test/data/valid_swagger/json/swagger2-with-params.json new file mode 100644 index 0000000..3c5ab4a --- /dev/null +++ b/test/data/valid_swagger/json/swagger2-with-params.json @@ -0,0 +1,48 @@ +{ + "swagger": "2.0", + "info": { + "description": "My API", + "version": "1.0.0", + "title": "Awesome Pets API", + "termsOfService": "http://www.domain.com", + "contact": { + "name": "support@domain.com" + } + }, + "basePath": "/", + "schemes": [ + "http" + ], + "paths": { + "/owner/{ownerId}/pet/{petId}": { + "post": { + "summary": "Find pets belonging to a owner", + "description": "", + "operationId": "findPetsOfOwners", + "parameters": [{ + "in": "path", + "name": "ownerId", + "description": "Owner Id", + "required": true, + "type": "integer", + "default": "42" + }, { + "in": "path", + "name": "petId", + "description": "Pet Id", + "required": true, + "type": "integer" + + }], + "responses": { + "200": { + "description": "Pet found successfully.", + "schema": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + } +} diff --git a/test/data/valid_swagger/json/swagger2.json b/test/data/valid_swagger/json/swagger2.json new file mode 100644 index 0000000..66856fd --- /dev/null +++ b/test/data/valid_swagger/json/swagger2.json @@ -0,0 +1,54 @@ +{ + "swagger": "2.0", + "info": { + "description": "My API", + "version": "1.0.0", + "title": "My API", + "termsOfService": "http://www.domain.com", + "contact": { + "name": "support@domain.com" + } + }, + "basePath": "/", + "schemes": [ + "http" + ], + "paths": { + "Authorization/LoginAPI": { + "post": { + "summary": "Authenticates you to the system and produces a session token that will be used for future calls", + "description": "", + "operationId": "LoginAPI", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "parameters": [{ + "in": "formData", + "name": "UserName", + "description": "Login Username", + "required": true, + "type": "string" + + }, { + "in": "formData", + "name": "Password", + "description": "Password", + "required": true, + "type": "string" + + }], + "responses": { + "200": { + "description": "API Response with session ID if login is allowed", + "schema": { + "$ref": "#/definitions/Authorization" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger3.json b/test/data/valid_swagger/json/swagger3.json new file mode 100644 index 0000000..fd107bd --- /dev/null +++ b/test/data/valid_swagger/json/swagger3.json @@ -0,0 +1,29 @@ +{ + "swagger": "2.0", + "info": { + "title": "Simple API overview", + "version": "v2" + }, + "paths": { + "/": { + "get": { + "operationId": "listVersionsv2", + "summary": "List API versions", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "200 300 response" + }, + "300": { + "description": "200 300 response" + } + } + } + } + }, + "consumes": [ + "application/json" + ] + } \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger_aws.json b/test/data/valid_swagger/json/swagger_aws.json new file mode 100644 index 0000000..fcca702 --- /dev/null +++ b/test/data/valid_swagger/json/swagger_aws.json @@ -0,0 +1,219 @@ +{ + "swagger": "2.0", + "info": { + "title": "API Gateway / Cognito Sample API", + "description": "Pet store sample that uses Cognito Developer Authenticated Identities to generate credentials through a Java Lambda Function", + "version": "1.0.0" + }, + "host": "execute-api.us-east-1.amazonaws.com", + "schemes": [ + "https" + ], + "basePath": "/", + "produces": [ + "application/json" + ], + "paths": { + "/users": { + "post": { + "summary": "Registers a new user", + "description": "Creates a new user in the DynamoDB backend database and returns a set \nof temporary credentials to sign future requests.\n", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "NewUser", + "in": "body", + "description": "New user details.", + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "tags": [ + "Auth" + ], + "x-postman-meta": { + "currentHelper": "awsSigV4", + "helperAttributes": { + "accessKey": "{{aws_access_key_id}}", + "secretKey": "{{aws_secret_access_key}}", + "region": "eu-west-1", + "service": "execute-api", + "saveHelper": true + } + }, + "responses": { + "200": { + "description": "The username of the new user and set of temporary credentials", + "schema": { + "$ref": "#/definitions/RegisterUserResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "User": { + "properties": { + "username": { + "type": "string", + "description": "A unique username for the user" + }, + "password": { + "type": "string", + "description": "A password for the new user" + } + } + }, + "RegisterUserResponse": { + "properties": { + "username": { + "type": "string", + "description": "The username of the new user" + }, + "identityId": { + "type": "string", + "description": "The unique identifier for the new user" + }, + "token": { + "type": "string", + "description": "An OpenID token for the new user" + }, + "credentials": { + "properties": { + "accessKey": { + "type": "string", + "description": "Temporary access key to sign requests" + }, + "secretKey": { + "type": "string", + "description": "Temporary secret access key to sign requests" + }, + "sessionToken": { + "type": "string", + "description": "Tempoarary session token" + }, + "expiration": { + "type": "integer", + "description": "Expiration date of the temporary credentials in millis since 1/1/1970\n" + } + } + } + } + }, + "LoginUserResponse": { + "properties": { + "identityId": { + "type": "string", + "description": "The unique identifier for the new user" + }, + "token": { + "type": "string", + "description": "An OpenID token for the new user" + }, + "credentials": { + "properties": { + "accessKey": { + "type": "string", + "description": "Temporary access key to sign requests" + }, + "secretKey": { + "type": "string", + "description": "Temporary secret access key to sign requests" + }, + "sessionToken": { + "type": "string", + "description": "Tempoarary session token" + }, + "expiration": { + "type": "integer", + "description": "Expiration date of the temporary credentials in millis since 1/1/1970\n" + } + } + } + } + }, + "NewPet": { + "properties": { + "petType": { + "type": "string", + "description": "Free text pet type" + }, + "petName": { + "type": "string", + "description": "Free text pet name" + }, + "petAge": { + "type": "integer", + "description": "Age of the new pet" + } + } + }, + "NewPetResponse": { + "properties": { + "petId": { + "type": "string", + "description": "The generated unique identifier for the new pet" + } + } + }, + "Pet": { + "properties": { + "petId": { + "type": "string", + "description": "The generated unique identifier for the new pet" + }, + "petType": { + "type": "string", + "description": "Free text pet type" + }, + "petName": { + "type": "string", + "description": "Free text pet name" + }, + "petAge": { + "type": "integer", + "description": "Age of the new pet" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "Pet" + } + }, + "Error": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "fields": { + "type": "string" + } + } + } + } +} diff --git a/test/data/valid_swagger/json/swagger_aws_2.json b/test/data/valid_swagger/json/swagger_aws_2.json new file mode 100644 index 0000000..36622d1 --- /dev/null +++ b/test/data/valid_swagger/json/swagger_aws_2.json @@ -0,0 +1,219 @@ +{ + "swagger": "2.0", + "info": { + "title": "API Gateway / Cognito Sample API", + "description": "Pet store sample that uses Cognito Developer Authenticated Identities to generate credentials through a Java Lambda Function", + "version": "1.0.0" + }, + "host": "execute-api.us-east-1.amazonaws.com", + "schemes": [ + "https" + ], + "basePath": "/", + "produces": [ + "application/json" + ], + "consumes": [ + "application/json" + ], + "paths": { + "/users": { + "post": { + "summary": "Registers a new user", + "description": "Creates a new user in the DynamoDB backend database and returns a set \nof temporary credentials to sign future requests.\n", + "produces": [ + "text/json" + ], + "parameters": [ + { + "name": "NewUser", + "in": "body", + "description": "New user details.", + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "tags": [ + "Auth" + ], + "x-postman-meta": { + "currentHelper": "awsSigV4", + "helperAttributes": { + "accessKey": "{{aws_access_key_id}}", + "secretKey": "{{aws_secret_access_key}}", + "region": "eu-west-1", + "service": "execute-api", + "saveHelper": true + } + }, + "responses": { + "200": { + "description": "The username of the new user and set of temporary credentials", + "schema": { + "$ref": "#/definitions/RegisterUserResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "#/definitions/Error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "User": { + "properties": { + "username": { + "type": "string", + "description": "A unique username for the user" + }, + "password": { + "type": "string", + "description": "A password for the new user" + } + } + }, + "RegisterUserResponse": { + "properties": { + "username": { + "type": "string", + "description": "The username of the new user" + }, + "identityId": { + "type": "string", + "description": "The unique identifier for the new user" + }, + "token": { + "type": "string", + "description": "An OpenID token for the new user" + }, + "credentials": { + "properties": { + "accessKey": { + "type": "string", + "description": "Temporary access key to sign requests" + }, + "secretKey": { + "type": "string", + "description": "Temporary secret access key to sign requests" + }, + "sessionToken": { + "type": "string", + "description": "Tempoarary session token" + }, + "expiration": { + "type": "integer", + "description": "Expiration date of the temporary credentials in millis since 1/1/1970\n" + } + } + } + } + }, + "LoginUserResponse": { + "properties": { + "identityId": { + "type": "string", + "description": "The unique identifier for the new user" + }, + "token": { + "type": "string", + "description": "An OpenID token for the new user" + }, + "credentials": { + "properties": { + "accessKey": { + "type": "string", + "description": "Temporary access key to sign requests" + }, + "secretKey": { + "type": "string", + "description": "Temporary secret access key to sign requests" + }, + "sessionToken": { + "type": "string", + "description": "Tempoarary session token" + }, + "expiration": { + "type": "integer", + "description": "Expiration date of the temporary credentials in millis since 1/1/1970\n" + } + } + } + } + }, + "NewPet": { + "properties": { + "petType": { + "type": "string", + "description": "Free text pet type" + }, + "petName": { + "type": "string", + "description": "Free text pet name" + }, + "petAge": { + "type": "integer", + "description": "Age of the new pet" + } + } + }, + "NewPetResponse": { + "properties": { + "petId": { + "type": "string", + "description": "The generated unique identifier for the new pet" + } + } + }, + "Pet": { + "properties": { + "petId": { + "type": "string", + "description": "The generated unique identifier for the new pet" + }, + "petType": { + "type": "string", + "description": "Free text pet type" + }, + "petName": { + "type": "string", + "description": "Free text pet name" + }, + "petAge": { + "type": "integer", + "description": "Age of the new pet" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "Pet" + } + }, + "Error": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "fields": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/json/swagger_xyz.json b/test/data/valid_swagger/json/swagger_xyz.json new file mode 100644 index 0000000..bae3d2b --- /dev/null +++ b/test/data/valid_swagger/json/swagger_xyz.json @@ -0,0 +1,1058 @@ +{ + "swagger": "2.0", + "info": { + "version": "v1", + "title": "API", + "description": "\n", + "termsOfService": "http://xyz.in/about/legal/terms/api" + }, + "host": "localhost:8080", + "basePath": "/api/v1", + "schemes": ["http"], + "produces": ["application/json"], + "consumes": ["application/x-www-form-urlencoded"], + "tags": [ + { "name": "Source" }, + { "name": "Users" }, + { "name": "Roles" }, + { "name": "Capabilities" }, + { + "name": "Mapping", + "description": "Mapping can be performed in one of the two ways\n\n* Users and Roles alotted to the users.\n* Roles and capabilities alloted to the roles.\n" + }, + { "name": "Transaction" } + ], + "securityDefinitions": { + "key": { "type": "apiKey", "in": "query", "name": "api_key" } + }, + "parameters": { + "source-id": { + "name": "source-id", + "in": "path", + "description": "The source identifier number", + "type": "integer", + "required": true + }, + "user-id": { + "name": "user-id", + "in": "path", + "description": "The user identifier number", + "type": "integer", + "required": true + }, + "tag-name": { + "name": "tag-name", + "in": "path", + "description": "Tag name", + "type": "string", + "required": true + } + }, + "paths": { + "/source/": { + "get": { + "tags": ["Source"], + "description": "Get information about all sources.", + "responses": { + "200": { + "description": "The source object", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Source" } + } + } + } + }, + "post": { + "tags": ["Source"], + "description": "Add a source.", + "consumes": ["application/x-www-form-urlencoded"], + "parameters": [ + { + "name": "source_name", + "in": "formData", + "description": "New source name", + "required": true, + "type": "string" + }, + { + "name": "parent_source_id", + "in": "formData", + "description": "Parent Source ID", + "required": true, + "type": "integer" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { "id": { "type": "integer" } } + } + } + } + } + }, + "/source/{source-id}": { + "get": { + "parameters": [{ "$ref": "#/parameters/source-id" }], + "tags": ["Source"], + "description": "Get basic information about a source.", + "responses": { + "200": { + "description": "The source object", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Source" } + } + } + } + }, + "delete": { + "parameters": [{ "$ref": "#/parameters/source-id" }], + "tags": ["Source"], + "description": "Deletes source based on its ID.", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/users/": { + "get": { + "tags": ["Users"], + "description": "Get information about all users.", + "responses": { + "200": { + "description": "The user object", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/User" } + } + } + } + }, + "post": { + "tags": ["Users"], + "description": "Add a user.", + "consumes": ["application/x-www-form-urlencoded"], + "parameters": [ + { + "name": "name", + "in": "formData", + "description": "New user name", + "required": true, + "type": "string" + }, + { + "name": "client_id", + "in": "formData", + "description": "Client ID", + "required": true, + "type": "integer" + }, + { + "name": "user_source_id", + "in": "formData", + "description": "Source ID for user", + "required": true, + "type": "integer" + }, + { + "name": "address", + "in": "formData", + "description": "User address", + "required": false, + "type": "string" + }, + { + "name": "address_type", + "in": "formData", + "description": "User address type", + "required": false, + "type": "string" + }, + { + "name": "locality_id", + "in": "formData", + "description": "Locality ID", + "required": false, + "type": "integer" + }, + { + "name": "phone", + "in": "formData", + "description": "User's phone number", + "required": false, + "type": "integer" + }, + { + "name": "email", + "in": "formData", + "description": "User's email address", + "required": false, + "type": "string" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { "id": { "type": "integer" } } + } + } + } + } + }, + "/users/{user-id}": { + "get": { + "parameters": [{ "$ref": "#/parameters/user-id" }], + "tags": ["Users"], + "description": "Get basic information about a user.", + "responses": { + "200": { + "description": "The user object", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/User" } + } + } + } + }, + "put": { + "parameters": [ + { "$ref": "#/parameters/user-id" }, + { + "name": "name", + "in": "formData", + "description": "New user name", + "type": "string", + "required": true + } + ], + "tags": ["Users"], + "description": "Update user name based on his ID.", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + }, + "delete": { + "parameters": [{ "$ref": "#/parameters/user-id" }], + "tags": ["Users"], + "description": "Delete user based on his ID.", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/users/lookup": { + "get": { + "tags": ["Users"], + "description": "Get user id using user's mobile number or email. Atleast one parameter must be passed.", + "parameters": [ + { + "name": "phone", + "in": "formData", + "description": "User's phone number.", + "type": "integer" + }, + { + "name": "email", + "in": "formData", + "description": "User's email.", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "user_id": { "type": "integer" } } + } + } + } + } + }, + "/users/merge": { + "put": { + "parameters": [ + { + "name": "email", + "in": "formData", + "required": true, + "description": "User's email address", + "type": "string" + }, + { + "name": "phone", + "in": "formData", + "required": true, + "description": "User's phone number", + "type": "integer" + } + ], + "tags": ["Users"], + "description": "Merge users together based on email and phone number.", + "responses": { "200": { "description": "Merged" } } + } + }, + "/users/restaurant/{restaurant-id}": { + "get": { + "tags": ["Users"], + "description": "See the list of users belonging to a restaurant based on `restaurant-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/User" } + } + } + }, + "parameters": [ + { + "name": "restaurant-id", + "in": "path", + "description": "Restaurant ID.", + "type": "integer", + "required": true + } + ] + } + }, + "/users/source/{source-id}": { + "get": { + "tags": ["Users"], + "description": "See the list of users belonging to a restaurant based on `source-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/User" } + } + } + }, + "parameters": [ + { + "name": "source-id", + "in": "path", + "description": "Source ID.", + "type": "integer", + "required": true + } + ] + } + }, + "/address/{address-id}": { + "get": { + "parameters": [ + { + "name": "address-id", + "in": "path", + "description": "User's address ID", + "type": "integer", + "required": true + } + ], + "description": "Get address details of the user based on his address ID.\n", + "tags": ["Users"], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Address" } + } + } + } + }, + "put": { + "parameters": [ + { + "name": "address-id", + "in": "path", + "description": "User's address ID", + "type": "integer", + "required": true + } + ], + "tags": ["Users"], + "responses": { + "200": { + "description": "OK\n", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + }, + "delete": { + "parameters": [ + { + "name": "address-id", + "in": "path", + "description": "User's address ID", + "type": "integer", + "required": true + } + ], + "tags": ["Users"], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/users/restaurant/{restaurant-id}/transactions": { + "get": { + "tags": ["Transaction"], + "description": "List of Transactions made by users for a specific restaurant.", + "parameters": [ + { + "name": "restaurant-id", + "in": "path", + "description": "Restaurant's ID for which transactions need to be returned.", + "type": "integer", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Transaction" } + } + } + } + } + }, + "/users/{user-id}/transactions": { + "parameters": [ + { + "name": "user-id", + "in": "path", + "description": "User's ID", + "type": "integer", + "required": true + } + ], + "get": { + "tags": ["Transaction"], + "description": "List of Transactions made by User across LT platform based on phone number or email. Atleast one parameter is required.", + "parameters": [ + { + "name": "phone", + "in": "formData", + "description": "User's phone number.", + "type": "integer" + }, + { + "name": "email", + "in": "formData", + "description": "User's email address", + "type": "string" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Transaction" } + } + } + } + }, + "post": { + "tags": ["Transaction"], + "description": "Add a new transaction to the user based on `user-id` and return a `transaction-id`.", + "parameters": [ + { + "name": "transaction-date", + "in": "formData", + "description": "Date of transaction.", + "type": "string" + }, + { + "name": "outlet-id", + "in": "formData", + "description": "Outlet's ID", + "type": "integer" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { "id": { "type": "integer" } } + } + } + } + } + }, + "/capabilities": { + "get": { + "tags": ["Capabilities"], + "description": "List all the available capabilities.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Capabilities" } + } + } + } + }, + "post": { + "tags": ["Capabilities"], + "description": "Generate a new capability\n", + "parameters": [ + { + "name": "name", + "in": "formData", + "required": true, + "description": "Name of capability", + "type": "string" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { "id": { "type": "integer" } } + } + } + } + } + }, + "/capabilities/{capability-id}": { + "parameters": [ + { + "name": "capability-id", + "in": "path", + "required": true, + "description": "Capability ID", + "type": "integer" + } + ], + "get": { + "tags": ["Capabilities"], + "description": "Get a specific Capability.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Capabilities" } + } + } + } + }, + "put": { + "parameters": [ + { + "name": "name", + "in": "formData", + "required": true, + "description": "New name for capability", + "type": "string" + } + ], + "tags": ["Capabilities"], + "description": "Update a capability based on its `capability-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + }, + "delete": { + "tags": ["Capabilities"], + "description": "Delete a capability based on its `capability-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/roles": { + "get": { + "tags": ["Roles"], + "description": "List all the available roles.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Roles" } + } + } + } + }, + "post": { + "tags": ["Roles"], + "description": "Generate a new role", + "parameters": [ + { + "name": "name", + "in": "formData", + "description": "Name of the new role.", + "type": "string" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { "id": { "type": "integer" } } + } + } + } + } + }, + "/roles/{role-id}": { + "parameters": [ + { + "name": "role-id", + "in": "path", + "required": true, + "description": "Role ID", + "type": "integer" + } + ], + "get": { + "tags": ["Roles"], + "description": "Get a specific Role based on its `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Roles" } + } + } + } + }, + "put": { + "parameters": [ + { + "name": "name", + "in": "formData", + "required": true, + "description": "New name for role", + "type": "string" + } + ], + "tags": ["Roles"], + "description": "Update a role based on its `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + }, + "delete": { + "tags": ["Roles"], + "description": "Delete a role based on its `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/users/{user-id}/roles": { + "parameters": [ + { + "name": "user-id", + "description": "user identifier number", + "in": "path", + "type": "integer", + "required": true + } + ], + "get": { + "tags": ["Mapping"], + "description": "List all the available roles for the user identified by `user-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Roles" } + } + } + } + }, + "post": { + "parameters": [ + { + "name": "role-name", + "in": "formData", + "type": "string", + "description": "Name of the role" + }, + { + "name": "role-id", + "in": "formData", + "type": "integer", + "description": "Role ID" + } + ], + "tags": ["Mapping"], + "description": "Assign roles to a user based on `role-id` or `role-name`. Atleast one parameter needs to be provided.\n", + "responses": { "201": { "description": "Created" } } + }, + "delete": { + "tags": ["Mapping"], + "description": "Delete all roles assigned to a user based on `user-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/users/{user-id}/roles/{role-id}": { + "get": { + "parameters": [ + { + "name": "user-id", + "description": "user identifier number", + "in": "path", + "type": "integer", + "required": true + }, + { + "name": "role-id", + "in": "path", + "type": "integer", + "description": "role identifier number", + "required": true + } + ], + "tags": ["Mapping"], + "description": "Show a role assigned to a user based on `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Roles" } + } + } + } + }, + "delete": { + "parameters": [ + { + "name": "user-id", + "description": "user identifier number", + "in": "path", + "type": "integer", + "required": true + }, + { + "name": "role-id", + "in": "path", + "type": "integer", + "description": "role identifier number", + "required": true + } + ], + "tags": ["Mapping"], + "description": "Delete a role assigned to a user based on `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/roles/{role-id}/capabilities": { + "parameters": [ + { + "name": "role-id", + "description": "role identifier number", + "in": "path", + "type": "integer", + "required": true + } + ], + "get": { + "tags": ["Mapping"], + "description": "List all the available capabilities for the role identified by `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Capabilities" } + } + } + } + }, + "post": { + "parameters": [ + { + "name": "capability-name", + "in": "formData", + "type": "string", + "description": "Name of the capability" + }, + { + "name": "capability-id", + "in": "formData", + "type": "integer", + "description": "Capability ID" + } + ], + "tags": ["Mapping"], + "description": "Assign capabilities to a role based on `capability-id` or `capability-name`. Atleast one parameter needs to be provided.\n", + "responses": { "201": { "description": "Created" } } + }, + "delete": { + "tags": ["Mapping"], + "description": "Delete all capabilities assigned to the role based on `role-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + }, + "/roles/{role-id}/capabilities/{capability-id}": { + "get": { + "parameters": [ + { + "name": "role-id", + "description": "role identifier number", + "in": "path", + "type": "integer", + "required": true + }, + { + "name": "capability-id", + "in": "path", + "type": "integer", + "description": "capability identifier number", + "required": true + } + ], + "tags": ["Mapping"], + "description": "Show a capability assigned to a role based on `capability-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { "$ref": "#/definitions/Capabilities" } + } + } + } + }, + "delete": { + "parameters": [ + { + "name": "role-id", + "description": "role identifier number", + "in": "path", + "type": "integer", + "required": true + }, + { + "name": "capability-id", + "in": "path", + "type": "integer", + "description": "Capability ID", + "required": true + } + ], + "tags": ["Mapping"], + "description": "Delete a capability assigned to a role based on `capability-id`.\n", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { "message": { "type": "string" } } + } + } + } + } + } + }, + "definitions": { + "Source": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "source_name": { "type": "string" }, + "parent_source_id": { "type": "integer" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" } + } + }, + "User": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "client_id": { "type": "integer" }, + "user_source_id": { "type": "integer" }, + "name": { "type": "string" }, + "status": { "type": "string" }, + "user_account_id": { "type": "integer" }, + "user_account_type": { "type": "string" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "Address": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "user_id": { "type": "integer" }, + "address": { "type": "string" }, + "address_type": { "type": "string" }, + "locality_id": { "type": "integer" }, + "status": { "type": "string" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "Email": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "user_id": { "type": "integer" }, + "user_email": { "type": "string" }, + "status": { "type": "string" }, + "created_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "Phone": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "user_id": { "type": "integer" }, + "user_phone_number": { "type": "integer" }, + "status": { "type": "string" }, + "created_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "Transaction": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "user_id": { "type": "integer" }, + "user_address_id": { "type": "integer" }, + "source_id": { "type": "integer" }, + "user_phone_number_id": { "type": "integer" }, + "user_email_id": { "type": "integer" }, + "outlet_id": { "type": "integer" }, + "transaction_date": { "format": "date" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" } + } + }, + "Roles": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "name": { "type": "string" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "Capabilities": { + "type": "object", + "properties": { + "id": { "type": "integer" }, + "name": { "type": "string" }, + "created_at": { "format": "date-time" }, + "updated_at": { "format": "date-time" }, + "deleted_at": { "format": "date-time" } + } + }, + "User_Role_Mappings": { + "type": "object", + "properties": { + "user_id": { "type": "integer" }, + "role_id": { "type": "integer" } + } + }, + "Role_Capability_Mappings": { + "type": "object", + "properties": { + "role_id": { "type": "integer" }, + "capability_id": { "type": "integer" } + } + } + } +} diff --git a/test/data/valid_swagger/json/uber.json b/test/data/valid_swagger/json/uber.json new file mode 100644 index 0000000..9577828 --- /dev/null +++ b/test/data/valid_swagger/json/uber.json @@ -0,0 +1,370 @@ +{ + "swagger": "2.0", + "info": { + "title": "Uber API", + "description": "Move your app forward with the Uber API", + "version": "1.0.0" + }, + "host": "api.uber.com", + "schemes": [ + "https" + ], + "basePath": "/v1", + "produces": [ + "application/json" + ], + "paths": { + "/products": { + "get": { + "summary": "Product Types", + "description": "The Products endpoint returns information about the Uber products offered at a given location. The response includes the display name and other details about each product, and lists the products in the proper display order.", + "parameters": [ + { + "name": "latitude", + "in": "query", + "description": "Latitude component of location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "longitude", + "in": "query", + "description": "Longitude component of location.", + "required": true, + "type": "number", + "format": "double" + } + ], + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "An array of products", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Product" + } + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/estimates/price": { + "get": { + "summary": "Price Estimates", + "description": "The Price Estimates endpoint returns an estimated price range for each product offered at a given location. The price estimate is provided as a formatted string with the full price range and the localized currency symbol.

The response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for situations requiring currency conversion. When surge is active for a particular product, its surge_multiplier will be greater than 1, but the price estimate already factors in this multiplier.", + "parameters": [ + { + "name": "start_latitude", + "in": "query", + "description": "Latitude component of start location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "start_longitude", + "in": "query", + "description": "Longitude component of start location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "end_latitude", + "in": "query", + "description": "Latitude component of end location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "end_longitude", + "in": "query", + "description": "Longitude component of end location.", + "required": true, + "type": "number", + "format": "double" + } + ], + "tags": [ + "Estimates" + ], + "responses": { + "200": { + "description": "An array of price estimates by product", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/PriceEstimate" + } + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/estimates/time": { + "get": { + "summary": "Time Estimates", + "description": "The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs.", + "parameters": [ + { + "name": "start_latitude", + "in": "query", + "description": "Latitude component of start location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "start_longitude", + "in": "query", + "description": "Longitude component of start location.", + "required": true, + "type": "number", + "format": "double" + }, + { + "name": "customer_uuid", + "in": "query", + "type": "string", + "format": "uuid", + "description": "Unique customer identifier to be used for experience customization." + }, + { + "name": "product_id", + "in": "query", + "type": "string", + "description": "Unique identifier representing a specific product for a given latitude & longitude." + } + ], + "tags": [ + "Estimates" + ], + "responses": { + "200": { + "description": "An array of products", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Product" + } + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/me": { + "get": { + "summary": "User Profile", + "description": "The User Profile endpoint returns information about the Uber user that has authorized with the application.", + "tags": [ + "User" + ], + "responses": { + "200": { + "description": "Profile information for a user", + "schema": { + "$ref": "#/definitions/Profile" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/history": { + "get": { + "summary": "User Activity", + "description": "The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.

The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary.", + "parameters": [ + { + "name": "offset", + "in": "query", + "type": "integer", + "format": "int32", + "description": "Offset the list of returned results by this amount. Default is zero." + }, + { + "name": "limit", + "in": "query", + "type": "integer", + "format": "int32", + "description": "Number of items to retrieve. Default is 5, maximum is 100." + } + ], + "tags": [ + "User" + ], + "responses": { + "200": { + "description": "History information for the given user", + "schema": { + "$ref": "#/definitions/Activities" + } + }, + "default": { + "description": "Unexpected error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + } + }, + "definitions": { + "Product": { + "properties": { + "product_id": { + "type": "string", + "description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles." + }, + "description": { + "type": "string", + "description": "Description of product." + }, + "display_name": { + "type": "string", + "description": "Display name of product." + }, + "capacity": { + "type": "string", + "description": "Capacity of product. For example, 4 people." + }, + "image": { + "type": "string", + "description": "Image URL representing the product." + } + } + }, + "PriceEstimate": { + "properties": { + "product_id": { + "type": "string", + "description": "Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles" + }, + "currency_code": { + "type": "string", + "description": "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code." + }, + "display_name": { + "type": "string", + "description": "Display name of product." + }, + "estimate": { + "type": "string", + "description": "Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or \"Metered\" for TAXI." + }, + "low_estimate": { + "type": "number", + "description": "Lower bound of the estimated price." + }, + "high_estimate": { + "type": "number", + "description": "Upper bound of the estimated price." + }, + "surge_multiplier": { + "type": "number", + "description": "Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier." + } + } + }, + "Profile": { + "properties": { + "first_name": { + "type": "string", + "description": "First name of the Uber user." + }, + "last_name": { + "type": "string", + "description": "Last name of the Uber user." + }, + "email": { + "type": "string", + "description": "Email address of the Uber user" + }, + "picture": { + "type": "string", + "description": "Image URL of the Uber user." + }, + "promo_code": { + "type": "string", + "description": "Promo code of the Uber user." + } + } + }, + "Activity": { + "properties": { + "uuid": { + "type": "string", + "description": "Unique identifier for the activity" + } + } + }, + "Activities": { + "properties": { + "offset": { + "type": "integer", + "format": "int32", + "description": "Position in pagination." + }, + "limit": { + "type": "integer", + "format": "int32", + "description": "Number of items to retrieve (100 max)." + }, + "count": { + "type": "integer", + "format": "int32", + "description": "Total number of items available." + }, + "history": { + "type": "array", + "items": { + "$ref": "#/definitions/Activity" + } + } + } + }, + "Error": { + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + }, + "fields": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/test/data/valid_swagger/yaml/instagram.yaml b/test/data/valid_swagger/yaml/instagram.yaml new file mode 100644 index 0000000..44d2b50 --- /dev/null +++ b/test/data/valid_swagger/yaml/instagram.yaml @@ -0,0 +1,1059 @@ +swagger: '2.0' +################################################################################ +# API Information # +################################################################################ +info: + version: v1 + title: Instagram API + description: | + The first version of the Instagram API is an exciting step forward towards + making it easier for users to have open access to their data. We created it + so that you can surface the amazing content Instagram users share every + second, in fun and innovative ways. + + Build something great! + + Once you've + [registered your client](http://instagram.com/developer/register/) it's easy + to start requesting data from Instagram. + + All endpoints are only accessible via https and are located at + `api.instagram.com`. For instance: you can grab the most popular photos at + the moment by accessing the following URL with your client ID + (replace CLIENT-ID with your own): + ``` + https://api.instagram.com/v1/media/popular?client_id=CLIENT-ID + ``` + You're best off using an access_token for the authenticated user for each + endpoint, though many endpoints don't require it. + In some cases an access_token will give you more access to information, and + in all cases, it means that you are operating under a per-access_token limit + vs. the same limit for your single client_id. + + + ## Limits + Be nice. If you're sending too many requests too quickly, we'll send back a + `503` error code (server unavailable). + You are limited to 5000 requests per hour per `access_token` or `client_id` + overall. Practically, this means you should (when possible) authenticate + users so that limits are well outside the reach of a given user. + + ## Deleting Objects + We do our best to have all our URLs be + [RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer). + Every endpoint (URL) may support one of four different http verbs. GET + requests fetch information about an object, POST requests create objects, + PUT requests update objects, and finally DELETE requests will delete + objects. + + Since many old browsers don't support PUT or DELETE, we've made it easy to + fake PUTs and DELETEs. All you have to do is do a POST with _method=PUT or + _method=DELETE as a parameter and we will treat it as if you used PUT or + DELETE respectively. + + ## Structure + + ### The Envelope + Every response is contained by an envelope. That is, each response has a + predictable set of keys with which you can expect to interact: + ```json + { + "meta": { + "code": 200 + }, + "data": { + ... + }, + "pagination": { + "next_url": "...", + "next_max_id": "13872296" + } + } + ``` + + #### META + The meta key is used to communicate extra information about the response to + the developer. If all goes well, you'll only ever see a code key with value + 200. However, sometimes things go wrong, and in that case you might see a + response like: + ```json + { + "meta": { + "error_type": "OAuthException", + "code": 400, + "error_message": "..." + } + } + ``` + + #### DATA + The data key is the meat of the response. It may be a list or dictionary, + but either way this is where you'll find the data you requested. + #### PAGINATION + Sometimes you just can't get enough. For this reason, we've provided a + convenient way to access more data in any request for sequential data. + Simply call the url in the next_url parameter and we'll respond with the + next set of data. + ```json + { + ... + "pagination": { + "next_url": "https://api.instagram.com/v1/tags/puppy/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&max_id=13872296", + "next_max_id": "13872296" + } + } + ``` + On views where pagination is present, we also support the "count" parameter. + Simply set this to the number of items you'd like to receive. Note that the + default values should be fine for most applications - but if you decide to + increase this number there is a maximum value defined on each endpoint. + + ### JSONP + If you're writing an AJAX application, and you'd like to wrap our response + with a callback, all you have to do is specify a callback parameter with + any API call: + ``` + https://api.instagram.com/v1/tags/coffee/media/recent?access_token=fb2e77d.47a0479900504cb3ab4a1f626d174d2d&callback=callbackFunction + ``` + Would respond with: + ```js + callbackFunction({ + ... + }); + ``` + termsOfService: http://instagram.com/about/legal/terms/api + +################################################################################ +# Host, Base Path, Schemes and Content Types # +################################################################################ +host: api.instagram.com +basePath: /v1 +schemes: + - https +produces: + - application/json +consumes: + - application/json + +################################################################################ +# Tags # +################################################################################ +tags: + - name: Users + - name: Relationships + description: | + Relationships are expressed using the following terms: + + **outgoing_status**: Your relationship to the user. Can be "follows", + "requested", "none". + **incoming_status**: A user's relationship to you. Can be "followed_by", + "requested_by", "blocked_by_you", "none". + - name: Media + description: | + At this time, uploading via the API is not possible. We made a conscious + choice not to add this for the following reasons: + + * Instagram is about your life on the go – we hope to encourage photos + from within the app. + * We want to fight spam & low quality photos. Once we allow uploading + from other sources, it's harder to control what comes into the Instagram + ecosystem. All this being said, we're working on ways to ensure users + have a consistent and high-quality experience on our platform. + - name: Commnts + - name: Likes + - name: Tags + - name: Location + - name: Subscribtions + +################################################################################ +# Security # +################################################################################ +securityDefinitions: + oauth: + type: oauth2 + flow: implicit + authorizationUrl: https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token + scopes: + basic: | + to read any and all data related to a user (e.g. following/followed-by + lists, photos, etc.) (granted by default) + comments: to create or delete comments on a user’s behalf + relationships: to follow and unfollow users on a user’s behalf + likes: to like and unlike items on a user’s behalf + key: + type: apiKey + in: query + name: access_token +security: + - oauth: + - basic + - comments + - relationships + - likes + - key: [] + +################################################################################ +# Parameters # +################################################################################ +parameters: + user-id: + name: user-id + in: path + description: The user identifier number + type: number + required: true + tag-name: + name: tag-name + in: path + description: Tag name + type: string + required: true + +################################################################################ +# Paths # +################################################################################ +paths: + /users/{user-id}: + parameters: + - $ref: '#/parameters/user-id' + get: + security: + - key: [] + - oauth: + - basic + tags: + - Users + description: Get basic information about a user. + responses: + 200: + description: The user object + schema: + type: object + properties: + data: + $ref: '#/definitions/User' + + /users/self/feed: + get: + tags: + - Users + description: See the authenticated user's feed. + parameters: + - name: count + in: query + description: Count of media to return. + type: integer + - name: max_id + in: query + description: Return media earlier than this max_id.s + type: integer + - name: min_id + in: query + description: Return media later than this min_id. + + type: integer + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Media' + + /users/{user-id}/media/recent: + parameters: + - $ref: '#/parameters/user-id' + get: + tags: + - Users + responses: + 200: + description: | + Get the most recent media published by a user. To get the most recent + media published by the owner of the access token, you can use `self` + instead of the `user-id`. + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Media' + parameters: + - name: count + in: query + description: Count of media to return. + type: integer + - name: max_timestamp + in: query + description: Return media before this UNIX timestamp. + type: integer + - name: min_timestamp + in: query + description: Return media after this UNIX timestamp. + type: integer + - name: min_id + in: query + description: Return media later than this min_id. + type: string + - name: max_id + in: query + description: Return media earlier than this max_id. + type: string + + /users/self/media/liked: + get: + tags: + - Users + description: | + See the list of media liked by the authenticated user. + Private media is returned as long as the authenticated user + has permissionto view that media. Liked media lists are only + available for the currently authenticated user. + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Media' + parameters: + - name: count + in: query + description: Count of media to return. + type: integer + - name: max_like_id + in: query + description: Return media liked before this id. + type: integer + + /users/search: + get: + tags: + - Users + description: Search for a user by name. + parameters: + - name: q + in: query + description: A query string + type: string + required: true + - name: count + in: query + description: Number of users to return. + type: string + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + + /users/{user-id}/follows: + parameters: + - $ref: '#/parameters/user-id' + get: + tags: + - Relationships + description: Get the list of users this user follows. + responses: + 200: + description: OK + schema: + properties: + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + + /users/{user-id}/followed-by: + parameters: + - $ref: '#/parameters/user-id' + get: + tags: + - Relationships + description: Get the list of users this user is followed by. + responses: + 200: + description: OK + schema: + properties: + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + + /users/self/requested-by: + get: + tags: + - Relationships + description: | + List the users who have requested this user's permission to follow. + responses: + 200: + description: OK + schema: + properties: + meta: + properties: + code: + type: integer + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + + /users/{user-id}/relationship: + parameters: + - $ref: '#/parameters/user-id' + post: + tags: + - Relationships + description: | + Modify the relationship between the current user and thetarget user. + security: + - oauth: + - relationships + parameters: + - name: action + in: body + description: One of follow/unfollow/block/unblock/approve/ignore. + schema: + type: string + enum: + - follow + - unfollow + - block + - unblock + - approve + + responses: + 200: + description: OK + schema: + properties: + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + + /media/{media-id}: + parameters: + - name: media-id + in: path + description: The media ID + type: integer + required: true + get: + tags: + - Media + description: | + Get information about a media object. + The returned type key will allow you to differentiate between `image` + and `video` media. + + Note: if you authenticate with an OAuth Token, you will receive the + `user_has_liked` key which quickly tells you whether the current user + has liked this media item. + responses: + 200: + description: OK + schema: + $ref: '#/definitions/Media' + + /media1/{shortcode}: #FIXME: correct path is /media/{shortcode} + parameters: + - name: shortcode + in: path + description: The media shortcode + type: string + required: true + get: + tags: + - Media + description: | + This endpoint returns the same response as **GET** `/media/media-id`. + + A media object's shortcode can be found in its shortlink URL. + An example shortlink is `http://instagram.com/p/D/` + Its corresponding shortcode is D. + + responses: + 200: + description: OK + schema: + $ref: '#/definitions/Media' + + /media/search: + get: + tags: + - Media + description: | + Search for media in a given area. The default time span is set to 5 + days. The time span must not exceed 7 days. Defaults time stamps cover + the last 5 days. Can return mix of image and video types. + + parameters: + - name: LAT + description: | + Latitude of the center search coordinate. If used, lng is required. + type: number + in: query + - name: MIN_TIMESTAMP + description: | + A unix timestamp. All media returned will be taken later than + this timestamp. + type: integer + in: query + - name: LNG + description: | + Longitude of the center search coordinate. If used, lat is required. + type: number + in: query + - name: MAX_TIMESTAMP + description: | + A unix timestamp. All media returned will be taken earlier than this + timestamp. + type: integer + in: query + - name: DISTANCE + description: Default is 1km (distance=1000), max distance is 5km. + type: integer + maximum: 5000 + default: 1000 + in: query + responses: + 200: + description: OK + schema: + type: object + description: List of all media with added `distance` property + properties: + data: + type: array + items: + allOf: + - $ref: '#/definitions/Media' + - + properties: + distance: + type: number + + /media/popular: + get: + tags: + - Media + description: | + Get a list of what media is most popular at the moment. + Can return mix of image and video types. + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Media' + + /media/{media-id}/comments: + parameters: + - name: media-id + in: path + description: Media ID + type: integer + required: true + get: + tags: + - Comments + description: | + Get a list of recent comments on a media object. + responses: + 200: + description: OK + schema: + properties: + meta: + properties: + code: + type: number + data: + type: array + items: + $ref: '#/definitions/Comment' + post: + tags: + - Comments + description: | + Create a comment on a media object with the following rules: + + * The total length of the comment cannot exceed 300 characters. + * The comment cannot contain more than 4 hashtags. + * The comment cannot contain more than 1 URL. + * The comment cannot consist of all capital letters. + security: + - oauth: + - comments + parameters: + - name: TEXT + description: | + Text to post as a comment on the media object as specified in + media-id. + in: body + schema: + type: number + responses: + 200: + description: OK + schema: + type: object + properties: + meta: + properties: + code: + type: number + data: + type: object + delete: + tags: + - Comments + description: | + Remove a comment either on the authenticated user's media object or + authored by the authenticated user. + responses: + 200: + description: OK + schema: + type: object + properties: + meta: + properties: + code: + type: number + data: + type: object + + /media/{media-id}/likes: + parameters: + - name: media-id + in: path + description: Media ID + type: integer + required: true + get: + tags: + - Likes + description: | + Get a list of users who have liked this media. + responses: + 200: + description: OK + schema: + properties: + meta: + properties: + code: + type: number + data: + type: array + items: + $ref: '#/definitions/Like' + post: + tags: + - Likes + description: Set a like on this media by the currently authenticated user. + security: + - oauth: + - comments + responses: + 200: + description: OK + schema: + type: object + properties: + meta: + properties: + code: + type: number + data: + type: object + delete: + tags: + - Likes + description: | + Remove a like on this media by the currently authenticated user. + responses: + 200: + description: OK + schema: + type: object + properties: + meta: + properties: + code: + type: number + data: + type: object + + /tags/{tag-name}: + parameters: + - $ref: '#/parameters/tag-name' + get: + tags: + - Tags + description: Get information about a tag object. + responses: + 200: + description: OK + schema: + $ref: '#/definitions/Tag' + + /tags/{tag-name}/media/recent: + parameters: + - $ref: '#/parameters/tag-name' + get: + tags: + - Tags + description: | + Get a list of recently tagged media. Use the `max_tag_id` and + `min_tag_id` parameters in the pagination response to paginate through + these objects. + responses: + 200: + description: OK + schema: + properties: + data: + type: array + items: + $ref: '#/definitions/Tag' + + /tags/search: + get: + tags: + - Tags + parameters: + - name: q + description: | + A valid tag name without a leading #. (eg. snowy, nofilter) + in: query + type: string + responses: + 200: + description: OK + schema: + type: object + properties: + meta: + properties: + code: + type: integer + data: + type: array + items: + $ref: '#/definitions/Tag' + + /locations/{location-id}: + parameters: + - name: location-id + description: Location ID + in: path + type: integer + required: true + get: + tags: + - Location + description: Get information about a location. + responses: + 200: + description: OK + schema: + type: object + properties: + data: + $ref: '#/definitions/Location' + + /locations/{location-id}/media/recent: + parameters: + - name: location-id + description: Location ID + in: path + type: integer + required: true + get: + tags: + - Location + description: Get a list of recent media objects from a given location. + parameters: + - name: max_timestamp + in: query + description: Return media before this UNIX timestamp. + type: integer + - name: min_timestamp + in: query + description: Return media after this UNIX timestamp. + type: integer + - name: min_id + in: query + description: Return media later than this min_id. + type: string + - name: max_id + in: query + description: Return media earlier than this max_id. + type: string + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Media' + + /locations/search: + get: + tags: + - Location + description: Search for a location by geographic coordinate. + parameters: + - name: distance + in: query + description: Default is 1000m (distance=1000), max distance is 5000. + type: integer + + - name: facebook_places_id + in: query + description: | + Returns a location mapped off of a Facebook places id. If used, a + Foursquare id and lat, lng are not required. + type: integer + + - name: foursquare_id + in: query + description: | + returns a location mapped off of a foursquare v1 api location id. + If used, you are not required to use lat and lng. Note that this + method is deprecated; you should use the new foursquare IDs with V2 + of their API. + type: integer + + - name: lat + in: query + description: | + atitude of the center search coordinate. If used, lng is required. + type: number + + - name: lng + in: query + description: | + ongitude of the center search coordinate. If used, lat is required. + type: number + + - name: foursquare_v2_id + in: query + description: | + Returns a location mapped off of a foursquare v2 api location id. If + used, you are not required to use lat and lng. + type: integer + responses: + 200: + description: OK + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/definitions/Location' + + /geographies/{geo-id}/media/recent: + parameters: + - name: geo-id + in: path + description: Geolocation ID + type: integer + required: true + get: + description: | + Get recent media from a geography subscription that you created. + **Note**: You can only access Geographies that were explicitly created + by your OAuth client. Check the Geography Subscriptions section of the + [real-time updates page](https://instagram.com/developer/realtime/). + When you create a subscription to some geography + that you define, you will be returned a unique geo-id that can be used + in this query. To backfill photos from the location covered by this + geography, use the [media search endpoint + ](https://instagram.com/developer/endpoints/media/). + parameters: + - name: count + in: query + description: Max number of media to return. + type: integer + - name: min_id + in: query + description: Return media before this `min_id`. + type: integer + responses: + 200: + description: OK + +################################################################################ +# Definitions # +################################################################################ +definitions: + User: + type: object + properties: + id: + type: integer + username: + type: string + full_name: + type: string + profile_picture: + type: string + bio: + type: string + website: + type: string + counts: + type: object + properties: + media: + type: integer + follows: + type: integer + follwed_by: + type: integer + Media: + type: object + properties: + created_time: + description: Epoc time (ms) + type: integer + type: + type: string + filter: + type: string + tags: + type: array + items: + $ref: '#/definitions/Tag' + id: + type: integer + user: + $ref: '#/definitions/MiniProfile' + users_in_photo: + type: array + items: + $ref: '#/definitions/MiniProfile' + location: + $ref: '#/definitions/Location' + comments:: + type: object + properties: + count: + type: integer + data: + type: array + items: + $ref: '#/definitions/Comment' + likes: + type: object + properties: + count: + type: integer + data: + type: array + items: + $ref: '#/definitions/MiniProfile' + images: + properties: + low_resolution: + $ref: '#/definitions/Image' + thumbnail: + $ref: '#/definitions/Image' + standard_resolution: + $ref: '#/definitions/Image' + videos: + properties: + low_resolution: + $ref: '#/definitions/Image' + standard_resolution: + $ref: '#/definitions/Image' + Location: + type: object + properties: + id: + type: string + name: + type: string + latitude: + type: number + longitude: + type: number + Comment: + type: object + properties: + id: + type: string + created_time: + type: string + text: + type: string + from: + $ref: '#/definitions/MiniProfile' + Like: + type: object + properties: + user_name: + type: string + first_name: + type: string + last_name: + type: string + type: + type: string + id: + type: string + Tag: + type: object + properties: + media_count: + type: integer + name: + type: string + Image: + type: object + properties: + width: + type: integer + height: + type: integer + url: + type: string + MiniProfile: + type: object + description: A shorter version of User for likes array + properties: + user_name: + type: string + full_name: + type: string + id: + type: integer + profile_picture: + type: string \ No newline at end of file diff --git a/test/data/valid_swagger/yaml/swagger_xyz.yaml b/test/data/valid_swagger/yaml/swagger_xyz.yaml new file mode 100644 index 0000000..67733b9 --- /dev/null +++ b/test/data/valid_swagger/yaml/swagger_xyz.yaml @@ -0,0 +1,1006 @@ +--- +swagger: '2.0' + +################################################################################ +# API Information # +################################################################################ +info: + version: v1 + title: API + description: | + termsOfService: http://xyz.in/about/legal/terms/api + +################################################################################ +# Host, Base Path, Schemes and Content Types # +################################################################################ + +host: localhost:8080 +basePath: /api/v1 +schemes: + - http +produces: + - application/json +consumes: + - appication/x-www-form-urlencoded + +################################################################################ +# Tags # +################################################################################ +tags: + - name: Source + - name: Users + - name: Roles + - name: Capabilities + - name: Mapping + description: | + Mapping can be performed in one of the two ways + + * Users and Roles alotted to the users. + * Roles and capabilities alloted to the roles. + - name: Transaction + +################################################################################ +# Security # +################################################################################ +securityDefinitions: + key: + type: apiKey + in: query + name: api_key + +################################################################################ +# Parameters # +################################################################################ +parameters: + source-id: + name: source-id + in: path + description: The source identifier number + type: integer + required: true + user-id: + name: user-id + in: path + description: The user identifier number + type: integer + required: true + tag-name: + name: tag-name + in: path + description: Tag name + type: string + required: true + +################################################################################ +# Paths # +################################################################################ +paths: + + /source/: + get: + tags: + - Source + description: Get information about all sources. + responses: + 200: + description: The source object + schema: + type: array + items: + $ref: '#/definitions/Source' + post: + tags: + - Source + description: Add a source. + consumes: + - application/x-www-form-urlencoded + parameters: + - name: source_name + in: formData + description: New source name + required: true + type: string + - name: parent_source_id + in: formData + description: Parent Source ID + required: true + type: integer + responses: + 201: + description: Created + schema: + type: object + properties: + id: + type: integer + + /source/{source-id}: + get: + parameters: + - $ref: '#/parameters/source-id' + tags: + - Source + description: Get basic information about a source. + responses: + 200: + description: The source object + schema: + type: array + items: + $ref: '#/definitions/Source' + delete: + parameters: + - $ref: '#/parameters/source-id' + tags: + - Source + description: Deletes source based on its ID. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + /users/: + get: + tags: + - Users + description: Get information about all users. + responses: + 200: + description: The user object + schema: + type: array + items: + $ref: '#/definitions/User' + post: + tags: + - Users + description: Add a user. + consumes: + - application/x-www-form-urlencoded + parameters: + - name: name + in: formData + description: New user name + required: true + type: string + - name: client_id + in: formData + description: Client ID + required: true + type: integer + - name: user_source_id + in: formData + description: Source ID for user + required: true + type: integer + - name: address + in: formData + description: User address + required: false + type: string + - name: address_type + in: formData + description: User address type + required: false + type: string + - name: locality_id + in: formData + description: Locality ID + required: false + type: integer + - name: phone + in: formData + description: User's phone number + required: false + type: integer + - name: email + in: formData + description: User's email address + required: false + type: string + responses: + 201: + description: Created + schema: + type: object + properties: + id: + type: integer + + /users/{user-id}: + get: + parameters: + - $ref: '#/parameters/user-id' + tags: + - Users + description: Get basic information about a user. + responses: + 200: + description: The user object + schema: + type: array + items: + $ref: '#/definitions/User' + put: + parameters: + - $ref: '#/parameters/user-id' + - name: name + in: formData + description: New user name + type: string + required: true + tags: + - Users + description: Update user name based on his ID. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + delete: + parameters: + - $ref: '#/parameters/user-id' + tags: + - Users + description: Delete user based on his ID. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + /users/lookup: + get: + tags: + - Users + description: Get user id using user's mobile number or email. Atleast one parameter must be passed. + parameters: + - name: phone + in: formData + description: User's phone number. + type: integer + - name: email + in: formData + description: User's email. + type: string + responses: + 200: + description: OK + schema: + type: object + properties: + user_id: + type: integer + + /users/merge: + put: + parameters: + - name: email + in: formData + required: true + description: User's email address + type: string + - name: phone + in: formData + required: true + description: User's phone number + type: integer + tags: + - Users + description: Merge users together based on email and phone number. + responses: + 200: + description: Merged + + /users/restaurant/{restaurant-id}: + get: + tags: + - Users + description: | + See the list of users belonging to a restaurant based on `restaurant-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/User' + parameters: + - name: restaurant-id + in: path + description: Restaurant ID. + type: integer + required: true + + /users/source/{source-id}: + get: + tags: + - Users + description: | + See the list of users belonging to a restaurant based on `source-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/User' + parameters: + - name: source-id + in: path + description: Source ID. + type: integer + required: true + + /address/{address-id}: + get: + parameters: + - name: address-id + in: path + description: User's address ID + type: integer + required: true + description: | + Get address details of the user based on his address ID. + tags: + - Users + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Address' + put: + parameters: + - name: address-id + in: path + description: User's address ID + type: integer + required: true + tags: + - Users + responses: + 200: + description: | + OK + schema: + type: object + properties: + message: + type: string + + delete: + parameters: + - name: address-id + in: path + description: User's address ID + type: integer + required: true + tags: + - Users + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + + /users/restaurant/{restaurant-id}/transactions: + get: + tags: + - Transaction + description: List of Transactions made by users for a specific restaurant. + parameters: + - name: restaurant-id + in: path + description: Restaurant's ID for which transactions need to be returned. + type: integer + required: true + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Transaction' + + /users/{user-id}/transactions: + parameters: + - name: user-id + in: path + description: User's ID + type: integer + required: true + get: + tags: + - Transaction + description: List of Transactions made by User across LT platform based on phone number or email. Atleast one parameter is required. + parameters: + - name: phone + in: formData + description: User's phone number. + type: integer + - name: email + in: formData + description: User's email address + type: string + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Transaction' + post: + tags: + - Transaction + description: Add a new transaction to the user based on `user-id` and return a `transaction-id`. + parameters: + - name: transaction-date + in: formData + description: Date of transaction. + type: string + - name: outlet-id + in: formData + description: Outlet's ID + type: integer + responses: + 201: + description: Created + schema: + type: object + properties: + id: + type: integer + + /capabilities: + get: + tags: + - Capabilities + description: | + List all the available capabilities. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Capabilities' + post: + tags: + - Capabilities + description: | + Generate a new capability + parameters: + - name: name + in: formData + required: true + description: Name of capability + type: string + responses: + 201: + description: Created + schema: + type: object + properties: + id: + type: integer + + /capabilities/{capability-id}: + parameters: + - name: capability-id + in: path + required: true + description: Capability ID + type: integer + get: + tags: + - Capabilities + description: | + Get a specific Capability. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Capabilities' + put: + parameters: + - name: name + in: formData + required: true + description: New name for capability + type: string + tags: + - Capabilities + description: | + Update a capability based on its `capability-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + delete: + tags: + - Capabilities + description: | + Delete a capability based on its `capability-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + /roles: + + get: + tags: + - Roles + description: | + List all the available roles. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Roles' + post: + tags: + - Roles + description: Generate a new role + parameters: + - name: name + in: formData + description: Name of the new role. + type: string + responses: + 201: + description: Created + schema: + type: object + properties: + id: + type: integer + + /roles/{role-id}: + parameters: + - name: role-id + in: path + required: true + description: Role ID + type: integer + get: + tags: + - Roles + description: | + Get a specific Role based on its `role-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Roles' + put: + parameters: + - name: name + in: formData + required: true + description: New name for role + type: string + tags: + - Roles + description: | + Update a role based on its `role-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + delete: + tags: + - Roles + description: | + Delete a role based on its `role-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + /users/{user-id}/roles: + parameters: + - name: user-id + description: user identifier number + in: path + type: integer + required: true + get: + tags: + - Mapping + description: | + List all the available roles for the user identified by `user-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Roles' + post: + parameters: + - name: role-name + in: formData + type: string + description: Name of the role + - name: role-id + in: formData + type: integer + description: Role ID + tags: + - Mapping + description: | + Assign roles to a user based on `role-id` or `role-name`. Atleast one parameter needs to be provided. + responses: + 201: + description: Created + delete: + tags: + - Mapping + description: | + Delete all roles assigned to a user based on `user-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + + /users/{user-id}/roles/{role-id}: + get: + parameters: + - name: user-id + description: user identifier number + in: path + type: integer + required: true + - name: role-id + in: path + type: integer + description: role identifier number + required: true + tags: + - Mapping + description: | + Show a role assigned to a user based on `role-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Roles' + + delete: + parameters: + - name: user-id + description: user identifier number + in: path + type: integer + required: true + - name: role-id + in: path + type: integer + description: role identifier number + required: true + tags: + - Mapping + description: | + Delete a role assigned to a user based on `role-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + + + /roles/{role-id}/capabilities: + parameters: + - name: role-id + description: role identifier number + in: path + type: integer + required: true + get: + tags: + - Mapping + description: | + List all the available capabilities for the role identified by `role-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Capabilities' + post: + parameters: + - name: capability-name + in: formData + type: string + description: Name of the capability + - name: capability-id + in: formData + type: integer + description: Capability ID + tags: + - Mapping + description: | + Assign capabilities to a role based on `capability-id` or `capability-name`. Atleast one parameter needs to be provided. + responses: + 201: + description: Created + delete: + tags: + - Mapping + description: | + Delete all capabilities assigned to the role based on `role-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + + /roles/{role-id}/capabilities/{capability-id}: + get: + parameters: + - name: role-id + description: role identifier number + in: path + type: integer + required: true + - name: capability-id + in: path + type: integer + description: capability identifier number + required: true + tags: + - Mapping + description: | + Show a capability assigned to a role based on `capability-id`. + responses: + 200: + description: OK + schema: + type: array + items: + $ref: '#/definitions/Capabilities' + delete: + parameters: + - name: role-id + description: role identifier number + in: path + type: integer + required: true + - name: capability-id + in: path + type: integer + description: Capability ID + required: true + tags: + - Mapping + description: | + Delete a capability assigned to a role based on `capability-id`. + responses: + 200: + description: OK + schema: + type: object + properties: + message: + type: string + + + + +################################################################################ +# Definitions # +################################################################################ +definitions: + + Source: + type: object + properties: + id: + type: integer + source_name: + type: string + parent_source_id: + type: integer + created_at: + format: date-time + updated_at: + format: date-time + User: + type: object + properties: + id: + type: integer + client_id: + type: integer + user_source_id: + type: integer + name: + type: string + status: + type: string + user_account_id: + type: integer + user_account_type: + type: string + created_at: + format: date-time + updated_at: + format: date-time + deleted_at: + format: date-time + + Address: + type: object + properties: + id: + type: integer + user_id: + type: integer + address: + type: string + address_type: + type: string + locality_id: + type: integer + status: + type: string + created_at: + format: date-time + updated_at: + format: date-time + deleted_at: + format: date-time + + Email: + type: object + properties: + id: + type: string + user_id: + type: integer + user_email: + type: string + status: + type: string + created_at: + format: date-time + deleted_at: + format: date-time + + Phone: + type: object + properties: + id: + type: integer + user_id: + type: integer + user_phone_number: + type: integer + status: + type: string + created_at: + format: date-time + deleted_at: + format: date-time + Transaction: + type: object + properties: + id: + type: integer + user_id: + type: integer + user_address_id: + type: integer + source_id: + type: integer + user_phone_number_id: + type: integer + user_email_id: + type: integer + outlet_id: + type: integer + transaction_date: + format: date + created_at: + format: date-time + updated_at: + format: date-time + + Roles: + type: object + properties: + id: + type: integer + name: + type: string + created_at: + format: date-time + updated_at: + format: date-time + deleted_at: + format: date-time + + Capabilities: + type: object + properties: + id: + type: integer + name: + type: string + created_at: + format: date-time + updated_at: + format: date-time + deleted_at: + format: date-time + + User_Role_Mappings: + type: object + properties: + user_id: + type: integer + role_id: + type: integer + + Role_Capability_Mappings: + type: object + properties: + role_id: + type: integer + capability_id: + type: integer diff --git a/test/data/valid_swagger/yaml/yaml_anchor.yaml b/test/data/valid_swagger/yaml/yaml_anchor.yaml new file mode 100644 index 0000000..f12fd87 --- /dev/null +++ b/test/data/valid_swagger/yaml/yaml_anchor.yaml @@ -0,0 +1,67 @@ +swagger: '2.0' +info: + title: YAML anchor issue + version: '1.0' +schemes: + - https +securityDefinitions: + Bearer: + description: Authorization 'Bearer' token + in: header + name: Authorization + type: apiKey +tags: + - name: client + description: Client resources +paths: + /client: + get: + deprecated: false + summary: Query Client + produces: &ref_0 + - application/json + security: &ref_1 + - Bearer: [] + tags: &ref_2 + - client + parameters: + - &ref_5 + in: query + name: page + type: integer + - &ref_6 + in: query + name: pageSize + type: integer + default: 25 + responses: + '200': + description: Search results + schema: + properties: + pagination: &ref_7 + type: object + properties: + page: + type: integer + pageSize: + type: integer + total: + type: integer + count: + type: integer + type: object + post: + deprecated: false + summary: Create a new Client + produces: *ref_0 + security: *ref_1 + tags: *ref_2 + responses: + '201': &ref_9 + description: Created + schema: + type: object + properties: + _id: + type: string diff --git a/test/unit/base.test.js b/test/unit/base.test.js index d39cdba..71e07f8 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -5,7 +5,9 @@ var expect = require('chai').expect, _ = require('lodash'), async = require('async'), VALID_OPENAPI_PATH = '../data/valid_openapi', - INVALID_OPENAPI_PATH = '../data/invalid_openapi'; + INVALID_OPENAPI_PATH = '../data/invalid_openapi', + SWAGGER_20_FOLDER_YAML = '../data/valid_swagger/yaml/', + SWAGGER_20_FOLDER_JSON = '../data/valid_swagger/json/'; describe('CONVERT FUNCTION TESTS ', function() { // these two covers remaining part of util.js @@ -1140,6 +1142,121 @@ describe('CONVERT FUNCTION TESTS ', function() { }); }); }); + describe('Converting swagger 2.0 files', function() { + it('should convert path paramters to postman-compatible paramters', function (done) { + const fileData = path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'swagger2-with-params.json'), + input = { + type: 'file', + data: fileData + }; + + Converter.convert(input, {}, function(err, convertResult) { + expect(err).to.be.null; + // Make sure that path params are updated and their respective default values + convertResult.output.forEach(function(element) { + expect(element.type).to.equal('collection'); + expect(element.data.item[0].request.url.path.indexOf(':ownerId') > -1).to.equal(true); + expect(element.data.item[0].request.url.path.indexOf(':petId') > -1).to.equal(true); + + let thisVar = element.data.item[0].request.url.variable[0]; + + expect(thisVar.type).to.equal('any'); + + expect(thisVar.value).to.equal('42'); + expect(thisVar.key).to.equal('ownerId'); + }); + done(); + }); + }); + + it('Should convert a swagger document with YAML anchors', function(done) { + const fileData = fs.readFileSync(path.join(__dirname, SWAGGER_20_FOLDER_YAML, 'yaml_anchor.yaml'), 'utf8'), + input = { + type: 'string', + data: fileData + }; + Converter.convert(input, {}, (error, result) => { + expect(error).to.be.null; + expect(result.result).to.equal(true); + expect(result.output.length).to.equal(1); + expect(result.output[0].type).to.have.equal('collection'); + expect(result.output[0].data).to.have.property('info'); + expect(result.output[0].data).to.have.property('item'); + }); + done(); + }); + + it('must read values consumes', function (done) { + const fileData = path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'swagger_aws_2.json'), + input = { + type: 'file', + data: fileData + }; + + Converter.convert(input, { requestName: 'url' }, (err, convertResult) => { + expect(err).to.be.null; + // Make sure that consumes and produces are processed + convertResult.output.forEach(function(element) { + expect(element.type).to.equal('collection'); + expect(JSON.stringify(element.data.item[0].request.header[0])).to + .equal('{"key":"Content-Type","value":"application/json"}'); + }); + done(); + }); + }); + + it('should convert a swagger object which only have a root path.', function(done) { + const fileData = JSON.parse( + fs.readFileSync(path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'swagger3.json'), 'utf8') + ), + input = { + type: 'json', + data: fileData + }; + + Converter.convert(input, {}, (err, result) => { + expect(result.result).to.equal(true); + expect(result.output.length).to.equal(1); + expect(result.output[0].type).to.have.equal('collection'); + expect(result.output[0].data).to.have.property('info'); + expect(result.output[0].data).to.have.property('item'); + + done(); + }); + }); + + it('should name the requests based on requestNameSource parameter, value=`URL`', function (done) { + const fileData = path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'swagger3.json'), + input = { + type: 'file', + data: fileData + }; + + Converter.convert(input, { requestNameSource: 'URL' }, (err, convertResult) => { + let request = convertResult.output[0].data.item[0].request; + + expect(err).to.be.null; + expect(request.name).to.equal('{{baseUrl}}/'); + done(); + }); + }); + + it('should name the requests based on requestNameSource parameter, value=`Fallback`', function (done) { + const fileData = path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'swagger3.json'), + input = { + type: 'file', + data: fileData + }; + + Converter.convert(input, { requestNameSource: 'Fallback' }, (err, convertResult) => { + let request = convertResult.output[0].data.item[0].request; + + expect(err).to.be.null; + expect(request.name).to.equal('List API versions'); + done(); + }); + }); + }); describe('requestNameSource option', function() { var pathPrefix = VALID_OPENAPI_PATH + '/test1.json', diff --git a/test/unit/swaggerSupport/schemaUtilsSwagger.test.js b/test/unit/swaggerSupport/schemaUtilsSwagger.test.js new file mode 100644 index 0000000..bb49750 --- /dev/null +++ b/test/unit/swaggerSupport/schemaUtilsSwagger.test.js @@ -0,0 +1,47 @@ +const { expect } = require('chai'), + concreteUtils = require('../../../lib/swaggerUtils/schemaUtilsSwagger'), + fs = require('fs'), + validSwaggerFolder = './test/data/valid_swagger', + invalidSwaggerFolder = './test/data/invalid_swagger'; + +describe('parseSpec method', function () { + it('should return true and a parsed specification', function () { + let fileContent = fs.readFileSync(validSwaggerFolder + '/json/sampleswagger.json', 'utf8'); + const parsedSpec = concreteUtils.parseSpec(fileContent, {}); + + expect(parsedSpec.result).to.be.true; + expect(parsedSpec.openapi.swagger).to.equal('2.0'); + }); + + it('should return false and info must have a title message', function () { + let fileContent = fs.readFileSync(invalidSwaggerFolder + '/invalid_no_info_title.json', 'utf8'); + const parsedSpec = concreteUtils.parseSpec(fileContent, {}); + + expect(parsedSpec.result).to.be.false; + expect(parsedSpec.reason).to.equal('Title, and version fields are required for the Info Object'); + }); + + it('should return false and swagger must have info object message', function () { + let fileContent = fs.readFileSync(invalidSwaggerFolder + '/invalid_no_info.json', 'utf8'); + const parsedSpec = concreteUtils.parseSpec(fileContent, {}); + + expect(parsedSpec.result).to.be.false; + expect(parsedSpec.reason).to.equal('The Swagger specification must have an \"info\" field'); + }); + + it('should return false and invalid version message', function () { + let fileContent = fs.readFileSync(invalidSwaggerFolder + '/invalid_wrong_swagger_version.json', 'utf8'); + const parsedSpec = concreteUtils.parseSpec(fileContent, {}); + + expect(parsedSpec.result).to.be.false; + expect(parsedSpec.reason).to.equal('The value of "swagger" field must be 2.0'); + }); + + it('should return false and no paths message', function () { + let fileContent = fs.readFileSync(invalidSwaggerFolder + '/invalid_no_paths.json', 'utf8'); + const parsedSpec = concreteUtils.parseSpec(fileContent, {}); + + expect(parsedSpec.result).to.be.false; + expect(parsedSpec.reason).to.equal('The Swagger specification must have a "paths" field'); + }); +}); diff --git a/test/unit/swaggerToOpenapi.test.js b/test/unit/swaggerToOpenapi.test.js new file mode 100644 index 0000000..0d0ca90 --- /dev/null +++ b/test/unit/swaggerToOpenapi.test.js @@ -0,0 +1,31 @@ +const { convertToOAS30IfSwagger } = require('../../lib/swaggerUtils/swaggerToOpenapi'), + fs = require('fs'), + path = require('path'), + SWAGGER_20_FOLDER_JSON = '../data/valid_swagger/json/', + SWAGGER_20_INVALID_FOLDER_JSON = '../data/invalid_swagger/', + utils = require('../../lib/swaggerUtils/schemaUtilsSwagger'), + expect = require('chai').expect; + +describe('Test swaggerToOpenapi method', function() { + it('Should convert a swagger file to an openapi', function() { + const fileSource = path.join(__dirname, SWAGGER_20_FOLDER_JSON + '/sampleswagger.json'), + fileData = fs.readFileSync(fileSource, 'utf8'), + parsedSpec = utils.parseSpec(fileData); + + convertToOAS30IfSwagger(utils, parsedSpec.openapi, (error, openapi) => { + expect(error).to.be.null; + expect(openapi.openapi).to.be.equal('3.0.0'); + }); + }); + + it('Should throw an error when swagger file is not complete', function() { + const fileSource = path.join(__dirname, SWAGGER_20_INVALID_FOLDER_JSON + '/invalid_no_info.json'), + fileData = fs.readFileSync(fileSource, 'utf8'), + parsedSpec = utils.parseSpec(fileData); + + convertToOAS30IfSwagger(utils, parsedSpec.openapi, (error, openapi) => { + expect(error.message).to.be.equal('Unsupported swagger/OpenAPI version: undefined'); + expect(openapi).to.be.undefined; + }); + }); +}); diff --git a/test/unit/versionUtils.test.js b/test/unit/versionUtils.test.js index 000526f..210136a 100644 --- a/test/unit/versionUtils.test.js +++ b/test/unit/versionUtils.test.js @@ -189,7 +189,7 @@ describe('filterOptionsByVersion method', function() { { id: 'optionC', name: 'option C', - supportedIn: ['3.1'], + supportedIn: ['3.1', '2.0'], default: 'A default value for option C' }, { @@ -239,4 +239,39 @@ describe('filterOptionsByVersion method', function() { return option.id; })).to.include.members(['optionA', 'optionB', 'optionD']); }); + + it('Should return the options supported in version 2.0', function() { + const optionsMock = [ + { + id: 'optionA', + name: 'option A', + supportedIn: ['2.0'], + default: 'A default value for option A' + }, + { + id: 'optionB', + name: 'option B', + supportedIn: ['3.0'], + default: 'A default value for option B' + }, + { + id: 'optionC', + name: 'option C', + supportedIn: ['3.1', '2.0'], + default: 'A default value for option C' + }, + { + id: 'optionD', + name: 'option D', + supportedIn: ['3.0', '3.1'], + default: 'A default value for option D' + } + ], + optionsFiltered = filterOptionsByVersion(optionsMock, '2.0'); + + expect(optionsFiltered).to.be.an('array'); + expect(optionsFiltered.map((option) => { + return option.id; + })).to.include.members(['optionC', 'optionA']); + }); }); diff --git a/test/unit/x20schemapack.test.js b/test/unit/x20schemapack.test.js new file mode 100644 index 0000000..37c9189 --- /dev/null +++ b/test/unit/x20schemapack.test.js @@ -0,0 +1,241 @@ +const SchemaPack = require('../..').SchemaPack, + expect = require('chai').expect, + fs = require('fs'), + path = require('path'), + SWAGGER_20_FOLDER_JSON = '../data/valid_swagger/json/'; + +describe('SchemaPack instance creation', function() { + it('Should create an instance of SchemaPack when input is a string', function() { + const fileSource = path.join(__dirname, SWAGGER_20_FOLDER_JSON + '/sampleswagger.json'), + fileData = fs.readFileSync(fileSource, 'utf8'), + input = { + type: 'string', + data: fileData + }, + schemapack = new SchemaPack(input); + + expect(schemapack); + }); + + it('Should create an instance of SchemaPack when input is a file', function() { + const fileSource = path.join(__dirname, SWAGGER_20_FOLDER_JSON + '/sampleswagger.json'), + input = { + type: 'file', + data: fileSource + }, + schemapack = new SchemaPack(input); + + expect(schemapack); + }); +}); + +describe('getMetaData method', function() { + it('Should return the provided input metadata', function() { + const fileSource = path.join(__dirname, SWAGGER_20_FOLDER_JSON + 'sampleswagger.json'), + input = { + type: 'file', + data: fileSource + }, + schemapack = new SchemaPack(input); + + schemapack.getMetaData((error, result) => { + expect(error).to.be.null; + expect(result.result).to.be.true; + expect(result.name).to.be.equal('Swagger Petstore'); + }); + }); +}); + +describe('Convert method', function() { + it('Should convert an example file from: ', function(done) { + const fileSource = path.join(__dirname, SWAGGER_20_FOLDER_JSON, 'sampleswagger.json'), + fileData = fs.readFileSync(fileSource, 'utf8'), + input = { + type: 'string', + data: fileData + }, + schemapack = new SchemaPack(input); + + schemapack.convert((error, result) => { + expect(error).to.be.null; + expect(result.result).to.be.true; + }); + done(); + }); +}); + +describe('Swagger 2.0 schemapack mergeAndValidate method', function() { + it('Should merge correctly the files in folder - petstore separated', function(done) { + let folderPath = path.join(__dirname, '../data/swaggerMultifile/petstore-separate-yaml'), + files = [], + array = [ + { fileName: folderPath + '/spec/swagger.yaml' }, + { fileName: folderPath + '/spec/Pet.yaml' }, + { fileName: folderPath + '/spec/parameters.yaml' }, + { fileName: folderPath + '/spec/NewPet.yaml' }, + { fileName: folderPath + '/common/Error.yaml' } + ]; + + array.forEach((item) => { + files.push({ + content: fs.readFileSync(item.fileName, 'utf8'), + fileName: item.fileName + }); + }); + + var schema = new SchemaPack({ type: 'folder', data: files }); + schema.mergeAndValidate((err, status) => { + if (err) { + expect.fail(null, null, err); + } + if (status.result) { + schema.convert((error, result) => { + if (error) { + expect.fail(null, null, err); + } + expect(result.result).to.equal(true); + expect(result.output.length).to.equal(1); + expect(result.output[0].type).to.have.equal('collection'); + expect(result.output[0].data).to.have.property('info'); + expect(result.output[0].data).to.have.property('item'); + done(); + }); + } + else { + expect.fail(null, null, status.reason); + done(); + } + }); + }); + + it('Should merge correctly the files in folder - basicExample', function(done) { + let folderPath = path.join(__dirname, '../data/swaggerMultifile/basicExample'), + files = [], + array = [ + { fileName: folderPath + '/index.yaml' }, + { fileName: folderPath + '/info.yaml' }, + { fileName: folderPath + '/paths.yaml' } + ]; + + array.forEach((item) => { + files.push({ + content: fs.readFileSync(item.fileName, 'utf8'), + fileName: item.fileName + }); + }); + + var schema = new SchemaPack({ type: 'folder', data: files }); + schema.mergeAndValidate((err, status) => { + if (err) { + expect.fail(null, null, err); + } + if (status.result) { + schema.convert((error, result) => { + if (error) { + expect.fail(null, null, err); + } + expect(result.result).to.equal(true); + expect(result.output.length).to.equal(1); + expect(result.output[0].type).to.have.equal('collection'); + expect(result.output[0].data).to.have.property('info'); + expect(result.output[0].data.info.name).to.equal('Sample API'); + expect(result.output[0].data).to.have.property('item'); + expect(result.output[0].data.item[0].request.name).to.equal('Returns a list of users.'); + done(); + }); + } + else { + expect.fail(null, null, status.reason); + done(); + } + }); + }); + + it('Should merge correctly the files in folder - uberTest', function(done) { + let folderPath = path.join(__dirname, '../data/swaggerMultifile/uberTest'), + files = [], + array = [ + { fileName: folderPath + '/index.yaml' }, + { fileName: folderPath + '/definitions/Activities.yaml' }, + { fileName: folderPath + '/definitions/Activity.yaml' }, + { fileName: folderPath + '/definitions/Error.yaml' }, + { fileName: folderPath + '/definitions/Product.yaml' }, + { fileName: folderPath + '/definitions/ProductList.yaml' }, + { fileName: folderPath + '/definitions/Profile.yaml' }, + { fileName: folderPath + '/definitions/PriceEstimate.yaml' } + ]; + + array.forEach((item) => { + files.push({ + content: fs.readFileSync(item.fileName, 'utf8'), + fileName: item.fileName + }); + }); + + var schema = new SchemaPack({ type: 'folder', data: files }); + schema.mergeAndValidate((err, status) => { + if (err) { + expect.fail(null, null, err); + } + if (status.result) { + schema.convert((error, result) => { + if (error) { + expect.fail(null, null, err); + } + expect(result.result).to.equal(true); + expect(result.output.length).to.equal(1); + expect(result.output[0].type).to.have.equal('collection'); + expect(result.output[0].data).to.have.property('info'); + expect(result.output[0].data).to.have.property('item'); + expect(result.output[0].data.item[0].response[0].body).to.include('product_id'); + expect(result.output[0].data.item[0].response[0].body).to.include('description'); + expect(result.output[0].data.item[1].item[0].response[0].body).to.include('product_id'); + expect(result.output[0].data.item[1].item[0].response[0].body).to.include('currency_code'); + expect(result.output[0].data.item[2].response[0].body).to.include('first_name'); + expect(result.output[0].data.item[2].response[0].body).to.include('last_name'); + expect(result.output[0].data.item[3].response[0].body).to.include('offset'); + expect(result.output[0].data.item[3].response[0].body).to.include('limit'); + expect(result.output[0].data.item[0].response[1].body).to.include('code'); + expect(result.output[0].data.item[0].response[1].body).to.include('message'); + done(); + }); + } + else { + expect.fail(null, null, status.reason); + done(); + } + }); + }); + + it('Should not merge because therer are 2 root files - multifile-two-root-files', function() { + let folderPath = path.join(__dirname, '../data/swaggerMultifile/multifile-two-root-files'), + files = [], + array = [ + { fileName: folderPath + '/index.yaml' }, + { fileName: folderPath + '/index1.yaml' }, + { fileName: folderPath + '/definitions/index.yaml' }, + { fileName: folderPath + '/definitions/User.yaml' }, + { fileName: folderPath + '/info/index.yaml' }, + { fileName: folderPath + '/info/index1.yaml' }, + { fileName: folderPath + '/paths/bar.yaml' }, + { fileName: folderPath + '/paths/foo.yaml' }, + { fileName: folderPath + '/paths/index.yaml' } + ]; + + array.forEach((item) => { + files.push({ + content: fs.readFileSync(item.fileName, 'utf8'), + fileName: item.fileName + }); + }); + + var schema = new SchemaPack({ type: 'folder', data: files }); + schema.mergeAndValidate((err, status) => { + if (err) { + expect.fail(null, null, err); + } + expect(status.result).to.equal(false); + expect(status.reason).to.be.equal('More than one root file not supported.'); + }); + }); +});