diff --git a/lib/types/api.js b/lib/types/api.js index dca973f7..c8838ca6 100644 --- a/lib/types/api.js +++ b/lib/types/api.js @@ -102,13 +102,19 @@ function SwaggerApi (definition, definitionRemotesResolved, definitionFullyResol debug(' Paths:'); // Create the Path objects - this.pathObjects = _.map(definitionFullyResolved.paths, function (pathDef, path) { - return new Path(that, - path, - _.get(definitionRemotesResolved, ['paths', path]), - pathDef, - ['paths', path]); - }); + this.pathObjects = _.reduce(definitionFullyResolved.paths, function (result, pathDef, path) { + // Filter out extensions + if (path.indexOf('x-') === 0) { + return result; + } + result.push(new Path( + that, + path, + _.get(definitionRemotesResolved, ['paths', path]), + pathDef, + ['paths', path])); + return result; + }, []); } /** diff --git a/lib/types/operation.js b/lib/types/operation.js index 646b53f0..2f63a707 100644 --- a/lib/types/operation.js +++ b/lib/types/operation.js @@ -120,15 +120,20 @@ function Operation (pathObject, method, definition, definitionFullyResolved, pat this._debug(' Responses:'); // Create response objects from responses defined in the operation definition - this.responseObjects = _.map(this.definitionFullyResolved.responses, function (responseDef, code) { + this.responseObjects = _.reduce(this.definitionFullyResolved.responses, function (result, responseDef, code) { + // Filter out extensions + if (code.indexOf('x-') === 0) { + return result; + } var rPath = pathToDefinition.concat(['responses', code]) - return new Response(that, - code, - _.get(that.pathObject.api.definitionRemotesResolved, rPath), - responseDef, - rPath); - }); + result.push(new Response(that, + code, + _.get(that.pathObject.api.definitionRemotesResolved, rPath), + responseDef, + rPath)); + return result; + }, []); this._debug(' Security:'); diff --git a/lib/validation/validators.js b/lib/validation/validators.js index 5d3eacc2..738da3d5 100644 --- a/lib/validation/validators.js +++ b/lib/validation/validators.js @@ -321,7 +321,9 @@ function validateReferences (api) { referenceable.push(JsonRefs.pathToPtr(['parameters', name])); }); - _.forEach(api.definitionFullyResolved.responses, function (def, name) { + var responses = stripExtensions(api.definitionFullyResolved.responses); + + _.forEach(responses, function (def, name) { referenceable.push(JsonRefs.pathToPtr(['responses', name])); }); @@ -397,7 +399,9 @@ function validateReferences (api) { // Identify references and validate missing references for non-JSON References (security) _.forEach(api.definitionFullyResolved.security, createSecurityProcessor(['security'])); - _.forEach(api.definitionFullyResolved.paths, function (pathDef, name) { + var pathObjects = stripExtensions(api.definitionFullyResolved.paths); + + _.forEach(pathObjects, function (pathDef, name) { var pPath = ['paths', name]; _.forEach(pathDef.security, createSecurityProcessor(pPath.concat('security'))); @@ -425,6 +429,25 @@ function validateReferences (api) { return response; } +/** + * Returns a new object with removed extension properties. + * This is important for paths and responses where every item in each of these + * collections are treated as individual paths or responses. Extensions should be + * ignored when iterating these items. + * @param {object} collection collection with extensions + * @returns {object} collection with stripped extensions + */ +function stripExtensions (collection) { + return _.reduce(collection, function (result, def, name) { + // Filter out extensions + if (name.indexOf('x-') === 0) { + return result; + } + result[name] = def; + return result; + }, {}); +} + /** * Validates all schema objects and schema-like objects (non-body path parameters). * @@ -476,6 +499,9 @@ function validateSchemaObjects (api) { } function validateResponses (responses, path) { + + responses = stripExtensions(responses); + _.forEach(responses, function (responseDef, name) { var rPath = path.concat(name); @@ -501,7 +527,9 @@ function validateSchemaObjects (api) { validateResponses(api.definitionFullyResolved.responses, ['responses']); // Validate paths and operations - _.forEach(api.definitionFullyResolved.paths, function (pathDef, path) { + var pathObjects = stripExtensions(api.definitionFullyResolved.paths); + + _.forEach(pathObjects, function (pathDef, path) { var pPath = ['paths', path]; // Validate path-level parameter definitions @@ -565,7 +593,9 @@ function validatePathsAndOperations (api) { return seenParameters; } - _.reduce(api.definitionFullyResolved.paths, function (metadata, pathDef, path) { + var pathObjects = stripExtensions(api.definitionFullyResolved.paths); + + _.reduce(pathObjects, function (metadata, pathDef, path) { var declaredPathParameters = []; var normalizedPath = path; var pPath = ['paths', path]; diff --git a/test/browser/documents/2.0/swagger.yaml b/test/browser/documents/2.0/swagger.yaml index afa959ba..a5aabc7b 100644 --- a/test/browser/documents/2.0/swagger.yaml +++ b/test/browser/documents/2.0/swagger.yaml @@ -31,6 +31,10 @@ tags: schemes: - "http" paths: + x-an-extension-null: + x-an-extension-primitive: 1 + x-an-extension-array: [] + x-an-extension-object: {} /pet: post: tags: @@ -52,6 +56,10 @@ paths: schema: $ref: "#/definitions/Pet" responses: + x-an-extension-null: + x-an-extension-primitive: 1 + x-an-extension-array: [] + x-an-extension-object: {} 405: description: "Invalid input" put: diff --git a/test/test-api.js b/test/test-api.js index cfdaa1f2..aba2b5a2 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -344,8 +344,12 @@ function runTests (mode) { }); describe('#getPaths', function () { - it('should return the expected path objects', function () { - assert.equal(swaggerApi.getPaths().length, Object.keys(swaggerApi.definitionFullyResolved.paths).length); + it('should return the expected path objects without extensions', function () { + var paths = Object.keys(swaggerApi.definitionFullyResolved.paths).filter(function (path) { + return path.indexOf('x-') !== 0; + }); + + assert.equal(swaggerApi.getPaths().length, paths.length); }); });