swagger-parser
Advanced tools
Comparing version 1.0.8 to 1.0.9
@@ -8,2 +8,5 @@ 'use strict'; | ||
// RegExp pattern for external pointers | ||
// (e.g. "http://company.com", "https://company.com", "./file.yaml", "../../file.yaml") | ||
var externalPointerPattern = /^(https?\:\/\/|\.)/i; | ||
@@ -116,3 +119,2 @@ module.exports = dereference; | ||
try { | ||
@@ -124,12 +126,3 @@ // If we've already resolved this pointer, then return the resolved value | ||
if (isInternalPointer(pointerValue)) { | ||
// "#/paths/users/responses/200" => "paths.users.responses.200" | ||
var deepProperty = pointerValue.substr(2).replace(/\//g, '.'); | ||
// Get the property value from the schema | ||
resolved = resultDeep(state.swaggerObject, deepProperty); | ||
state.resolvedPointers[pointerValue] = resolved; | ||
returnResolvedValue(null, resolved); | ||
} | ||
else if (isExternalPointer(pointerValue)) { | ||
if (isExternalPointer(pointerValue)) { | ||
// Set the resolved value to an empty object for now, so other reference pointers | ||
@@ -152,4 +145,14 @@ // can point to this object. Once we finish downloading the URL, we can update | ||
else { | ||
// Swagger allows a shorthand reference syntax (e.g. "Product" => "#/definitions/Product") | ||
resolved = _.result(state.swaggerObject.definitions, pointerValue); | ||
var propertyPath; | ||
if (pointerValue.indexOf('#/') === 0) { | ||
// "#/paths/users/responses/200" => "paths.users.responses.200" | ||
propertyPath = pointerValue.substr(2).replace(/\//g, '.'); | ||
} | ||
else { | ||
// "pet" => "definitions.pet" | ||
propertyPath = 'definitions.' + pointerValue.replace(/\//g, '.'); | ||
} | ||
// Get the property value from the schema | ||
resolved = resultDeep(state.swagger, propertyPath); | ||
state.resolvedPointers[pointerValue] = resolved; | ||
@@ -166,11 +169,2 @@ returnResolvedValue(null, resolved); | ||
/** | ||
* Determines whether the given $ref pointer value references a path in Swagger spec. | ||
* @returns {boolean} | ||
*/ | ||
function isInternalPointer(pointerValue) { | ||
return pointerValue && pointerValue.indexOf('#/') === 0; | ||
} | ||
/** | ||
* Determines whether the given $ref pointer value references an external file. | ||
@@ -180,3 +174,3 @@ * @returns {boolean} | ||
function isExternalPointer(pointerValue) { | ||
return pointerValue && (pointerValue.indexOf('http://') === 0 || pointerValue.indexOf('https://') === 0); | ||
return pointerValue && externalPointerPattern.test(pointerValue); | ||
} | ||
@@ -183,0 +177,0 @@ |
@@ -46,3 +46,3 @@ 'use strict'; | ||
var state = new State(); | ||
state.swaggerSourceDir = path.dirname(swaggerFile); | ||
state.baseDir = path.dirname(swaggerFile); | ||
state.options = options; | ||
@@ -55,3 +55,3 @@ | ||
state.swaggerObject = swaggerObject; | ||
state.swagger = swaggerObject; | ||
@@ -58,0 +58,0 @@ // Validate the version number |
257
lib/read.js
@@ -23,10 +23,12 @@ 'use strict'; | ||
try { | ||
// Parse the path and determine whether it's a file or a URL by its protocol | ||
// Resolve the path/URL relative to the Swagger file | ||
pathOrUrl = url.resolve(state.baseDir + '/', pathOrUrl); | ||
// Determine whether its a local file or a URL | ||
var parsedUrl = url.parse(pathOrUrl); | ||
if (isLocalFile(parsedUrl, state)) { | ||
read.file(urlToRelativePath(parsedUrl), state, callback); | ||
if (isLocalFile(parsedUrl)) { | ||
readFile(parsedUrl.href, state, callback); | ||
} | ||
else { | ||
read.url(parsedUrl.href, state, callback); | ||
readUrl(parsedUrl, state, callback); | ||
} | ||
@@ -37,136 +39,121 @@ } | ||
} | ||
}, | ||
} | ||
}; | ||
/** | ||
* Reads a JSON or YAML file from the local filesystem and returns the parsed POJO. | ||
* @param {string} filePath Local file path, relative to the Swagger file. | ||
* @param {State} state The state for the current parse operation | ||
* @param {function} callback function(err, parsedObject) | ||
*/ | ||
file: function(filePath, state, callback) { | ||
function errorHandler(err) { | ||
callback(util.error('Error opening file "%s": \n%s: %s \n\n%s', filePath, err.name, err.message, err.stack)); | ||
} | ||
/** | ||
* Reads a JSON or YAML file from the local filesystem and returns the parsed POJO. | ||
* @param {string} filePath The full, absolute path of the file (NOT RELATIVE!) | ||
* @param {State} state The state for the current parse operation | ||
* @param {function} callback function(err, parsedObject) | ||
*/ | ||
function readFile(filePath, state, callback) { | ||
function errorHandler(err) { | ||
callback(util.error('Error opening file "%s": \n%s: %s \n\n%s', filePath, err.name, err.message, err.stack)); | ||
} | ||
function parseError(err) { | ||
callback(util.syntaxError('Error parsing file "%s": \n%s: %s \n\n%s', filePath, err.name, err.message, err.stack)); | ||
} | ||
function parseError(err) { | ||
callback(util.syntaxError('Error parsing file "%s": \n%s: %s \n\n%s', filePath, err.name, err.message, err.stack)); | ||
} | ||
try { | ||
// Get the file path, relative to the Swagger file's directory | ||
filePath = path.resolve(state.swaggerSourceDir, filePath); | ||
try { | ||
debug('Reading file "%s"', filePath); | ||
debug('Reading file "%s"', filePath); | ||
fs.readFile(filePath, {encoding: 'utf8'}, function(err, data) { | ||
if (err) { | ||
return errorHandler(err); | ||
} | ||
fs.readFile(filePath, {encoding: 'utf8'}, function(err, data) { | ||
if (err) { | ||
return errorHandler(err); | ||
} | ||
try { | ||
state.files.push(filePath); | ||
callback(null, parseJsonOrYaml(filePath, data, state)); | ||
} | ||
catch (e) { | ||
parseError(e); | ||
} | ||
}); | ||
} | ||
catch (e) { | ||
errorHandler(e); | ||
} | ||
} | ||
try { | ||
state.files.push(filePath); | ||
callback(null, parseJsonOrYaml(filePath, data, state)); | ||
} | ||
catch (e) { | ||
parseError(e); | ||
} | ||
}); | ||
} | ||
catch (e) { | ||
errorHandler(e); | ||
} | ||
}, | ||
/** | ||
* Reads a JSON or YAML file from the a remote URL and returns the parsed POJO. | ||
* @param {Url} parsedUrl The full, parsed URL | ||
* @param {State} state The state for the current parse operation | ||
* @param {function} callback function(err, parsedObject) | ||
*/ | ||
function readUrl(parsedUrl, state, callback) { | ||
// NOTE: When HTTP errors occur, they can trigger multiple on('error') events, | ||
// So we need to make sure we only invoke the callback function ONCE. | ||
callback = _.once(callback); | ||
/** | ||
* Reads a JSON or YAML file from the a remote URL and returns the parsed POJO. | ||
* @param {string|Url} urlPath The file URL, relative to the Swagger file. | ||
* @param {State} state The state for the current parse operation | ||
* @param {function} callback function(err, parsedObject) | ||
*/ | ||
url: function(urlPath, state, callback) { | ||
var href = urlPath; | ||
function downloadError(err) { | ||
callback(util.error('Error downloading file "%s": \n%s: %s \n\n%s', parsedUrl.href, err.name, err.message, err.stack)); | ||
} | ||
// NOTE: When HTTP errors occur, they can trigger multiple on('error') events, | ||
// So we need to make sure we only invoke the callback function ONCE. | ||
callback = _.once(callback); | ||
function parseError(err) { | ||
callback(util.syntaxError('Error parsing file "%s": \n%s: %s \n\n%s', parsedUrl.href, err.name, err.message, err.stack)); | ||
} | ||
function downloadError(err) { | ||
callback(util.error('Error downloading file "%s": \n%s: %s \n\n%s', href, err.name, err.message, err.stack)); | ||
} | ||
try { | ||
var options = { | ||
host: parsedUrl.host, | ||
hostname: parsedUrl.hostname, | ||
port: parsedUrl.port, | ||
path: parsedUrl.path, | ||
auth: parsedUrl.auth, | ||
headers: {'Content-Type': 'application/json'} | ||
}; | ||
function parseError(err) { | ||
callback(util.syntaxError('Error parsing file "%s": \n%s: %s \n\n%s', href, err.name, err.message, err.stack)); | ||
} | ||
debug('Downloading file "%s"', parsedUrl.href); | ||
try { | ||
// Parse the URL, if it's not already parsed | ||
urlPath = url.parse(urlPath); | ||
var req = http.get(options, function(res) { | ||
var body = ''; | ||
if (isRelativeUrl(urlPath)) { | ||
// Resolve the url, relative to the Swagger file | ||
urlPath = url.parse(url.resolve(state.swaggerSourceDir + '/', urlToRelativePath(urlPath))); | ||
if (_.isFunction(res.setEncoding)) { | ||
res.setEncoding('utf8'); | ||
} | ||
href = urlPath.href; | ||
res.on('data', function(data) { | ||
body += data; | ||
}); | ||
var options = { | ||
host: urlPath.host, | ||
hostname: urlPath.hostname, | ||
port: urlPath.port, | ||
path: urlPath.path, | ||
auth: urlPath.auth, | ||
headers: {'Content-Type': 'application/json'} | ||
}; | ||
res.on('end', function() { | ||
if (res.statusCode >= 400) { | ||
return downloadError(new Error('HTTP ERROR ' + res.statusCode + ': ' + body)); | ||
} | ||
debug('Downloading file "%s"', href); | ||
var req = http.get(options, function(res) { | ||
var body = ''; | ||
if (_.isFunction(res.setEncoding)) { | ||
res.setEncoding('utf8'); | ||
try { | ||
state.urls.push(parsedUrl); | ||
callback(null, parseJsonOrYaml(parsedUrl.href, body, state)); | ||
} | ||
catch (e) { | ||
parseError(e); | ||
} | ||
}); | ||
res.on('data', function(data) { | ||
body += data; | ||
}); | ||
res.on('end', function() { | ||
if (res.statusCode >= 400) { | ||
return downloadError(new Error('HTTP ERROR ' + res.statusCode + ': ' + body)); | ||
} | ||
try { | ||
state.urls.push(urlPath); | ||
callback(null, parseJsonOrYaml(href, body, state)); | ||
} | ||
catch (e) { | ||
parseError(e); | ||
} | ||
}); | ||
res.on('error', function(e) { | ||
downloadError(e); | ||
}); | ||
res.on('error', function(e) { | ||
downloadError(e); | ||
}); | ||
}); | ||
if (_.isFunction(req.setTimeout)) { | ||
req.setTimeout(5000); | ||
} | ||
if (_.isFunction(req.setTimeout)) { | ||
req.setTimeout(5000); | ||
} | ||
req.on('timeout', function() { | ||
req.abort(); | ||
}); | ||
req.on('timeout', function() { | ||
req.abort(); | ||
}); | ||
req.on('error', function(e) { | ||
downloadError(e); | ||
}); | ||
} | ||
catch (e) { | ||
req.on('error', function(e) { | ||
downloadError(e); | ||
} | ||
}); | ||
} | ||
}; | ||
catch (e) { | ||
downloadError(e); | ||
} | ||
} | ||
@@ -186,6 +173,5 @@ | ||
* @param {Url} parsedUrl A parsed Url object | ||
* @param {State} state The state for the current parse operation | ||
* @returns {boolean} | ||
*/ | ||
function isLocalFile(parsedUrl, state) { | ||
function isLocalFile(parsedUrl) { | ||
if (isBrowser()) { | ||
@@ -196,12 +182,10 @@ // Local files aren't supported in browsers | ||
if (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') { | ||
// If the path exists locally, then treat the URL as a local file | ||
var relativePath = urlToRelativePath(parsedUrl); | ||
var filePath = path.resolve(state.swaggerSourceDir, relativePath); | ||
return fs.existsSync(filePath); | ||
// If the path exists locally, then treat the URL as a local file | ||
if (fs.existsSync(parsedUrl.pathname)) { | ||
return true; | ||
} | ||
// If it's anything other than "http" or "https", then assume it's a local file. | ||
// This includes "file://", "/absolute/paths", "relative/paths", and "c:\windows\paths" | ||
return true; | ||
// If all else fails, then determine based on the protocol | ||
// NOTE: The following are all considered local files: "file://path/to/file", "/path/to/file", "c:\path\to\file" | ||
return parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:'; | ||
} | ||
@@ -211,29 +195,2 @@ | ||
/** | ||
* Return the equivalent relative file path for a given url. | ||
* @param {Url} parsedUrl A parsed Url object | ||
* @returns {string} | ||
*/ | ||
function urlToRelativePath(parsedUrl) { | ||
if (isRelativeUrl(parsedUrl)) { | ||
// "http://../../file.yaml" => "../../file.yaml" | ||
return parsedUrl.href.substr(parsedUrl.protocol.length + 2); | ||
} | ||
// "http://localhost/folder/subfolder/file.yaml" => "folder/subfolder/file.yaml" | ||
return parsedUrl.pathname; | ||
} | ||
/** | ||
* Treats a URL as relative if its hostname is "." or ".." | ||
* NOTE: This is an undocumented feature that's only intended for cross-platform testing. Don't rely on it! | ||
* @param {Url} parsedUrl A parsed Url object | ||
* @returns {boolean} | ||
*/ | ||
function isRelativeUrl(parsedUrl) { | ||
return parsedUrl.host === '.' || parsedUrl.host === '..'; | ||
} | ||
/** | ||
* Parses a JSON or YAML string into a POJO. | ||
@@ -240,0 +197,0 @@ * @param {string} pathOrUrl |
@@ -11,6 +11,6 @@ 'use strict'; | ||
/** | ||
* The directory where the Swagger file is located | ||
* (used for resolving relative file references) | ||
* The directory of the Swagger file | ||
* (used as the base directory for resolving relative file references) | ||
*/ | ||
this.swaggerSourceDir = null; | ||
this.baseDir = null; | ||
@@ -24,9 +24,8 @@ /** | ||
/** | ||
* The entire SwaggerObject | ||
* (used for resolving schema references) | ||
* The Swagger object that is being parsed. | ||
*/ | ||
this.swaggerObject = null; | ||
this.swagger = null; | ||
/** | ||
* The full path and filename of files that have been read during the parsing operation. | ||
* The files that have been read during the parsing operation. | ||
* @type {string[]} | ||
@@ -37,3 +36,3 @@ */ | ||
/** | ||
* The parsed URLs that have been downloaded during the parsing operation. | ||
* The URLs that have been downloaded during the parsing operation. | ||
* @type {url.Url[]} | ||
@@ -44,3 +43,3 @@ */ | ||
/** | ||
* A map of resolved "$ref" pointers to their values | ||
* A map of resolved "$ref" pointers and values | ||
*/ | ||
@@ -47,0 +46,0 @@ this.resolvedPointers = {}; |
{ | ||
"name": "swagger-parser", | ||
"version": "1.0.8", | ||
"version": "1.0.9", | ||
"description": "Parses a JSON or YAML Swagger spec, validates it against the Swagger schema, and dereferences all $ref pointers", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2945471
23665