Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

swagger-parser

Package Overview
Dependencies
Maintainers
1
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

swagger-parser - npm Package Compare versions

Comparing version 1.0.11 to 2.0.0

lib/resolve.js

60

lib/defaults.js
/**
* The default parsing options.
* @name defaults
* @type {{parseYaml: boolean, dereferencePointers: boolean, dereferenceExternalPointers: boolean, validateSpec: boolean}}
* @type {{parseYaml: boolean, resolve$Refs: boolean, resolveExternal$Refs: boolean, validateSchema: boolean}}
*/
module.exports = {
/**
* Determines whether the parser will allow Swagger specs in YAML format.
* If set to `false`, then only JSON will be allowed. Defaults to `true`.
* @type {boolean}
*/
parseYaml: true,
/**
* Determines whether the parser will allow Swagger specs in YAML format.
* If set to `false`, then only JSON will be allowed. Defaults to `true`.
* @type {boolean}
*/
parseYaml: true,
/**
* Determines whether `$ref` pointers will be dereferenced.
* If set to `false`, then the resulting SwaggerObject will contain ReferenceObjects instead of the objects they reference.
* (see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-)
* Defaults to `true`.
* @type {boolean}
*/
dereferencePointers: true,
/**
* Determines whether `$ref` pointers will be dereferenced.
* If set to `false`, then the resulting Swagger object will contain ReferenceObjects instead of the objects they reference.
* (see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-)
* Defaults to `true`.
* @type {boolean}
*/
resolve$Refs: true,
/**
* Determines whether `$ref` pointers will be dereferenced if they point to external files (e.g. "http://company.com/my/schema.json").
* If set to `false`, then the resulting SwaggerObject will contain ReferenceObjects instead of the objects they reference.
* (see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-)
* Defaults to `true`.
* @type {boolean}
*/
dereferenceExternalPointers: true,
/**
* Determines whether `$ref` pointers will be dereferenced if they point to external files (e.g. "http://company.com/my/schema.json").
* If set to `false`, then the resulting Swagger object will contain ReferenceObjects instead of the objects they reference.
* (see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-)
* Defaults to `true`.
* @type {boolean}
*/
resolveExternal$Refs: true,
/**
* Determines whether the Swagger spec will be validated against the Swagger schema.
* If set to `false`, then the resulting SwaggerObject may be missing properties, have properties of the wrong data type, etc.
* Defaults to `true`.
* @type {boolean}
*/
validateSpec: true
/**
* Determines whether the API will be validated against the Swagger schema.
* If set to `false`, then the resulting Swagger object may be missing properties, have properties of the wrong data type, etc.
* Defaults to `true`.
* @type {boolean}
*/
validateSchema: true
};
'use strict';
var _ = require('lodash');
var url = require('url');
var read = require('./read');
var resolve = require('./resolve');
var util = require('./util');
var debug = require('./debug');
// RegExp pattern for external pointers
// (e.g. "http://company.com", "https://company.com", "./file.yaml", "../../file.yaml")
var externalPointerPattern = /^https?\:\/\/|^\.|\.yml$|\.yaml$|\.json$/i;

@@ -19,262 +15,37 @@ module.exports = dereference;

* with their corresponding object references.
* @param {object} obj
* @param {string} schemaPath
* @param {State} state
* @param {function} callback
*
* @param {SwaggerObject} api The Swagger API to dereference
* @param {State} state The state for the current parse operation
* @param {function} callback
*/
function dereference(obj, schemaPath, state, callback) {
// Do nothing if dereferencing is disabled
if (!state.options.dereferencePointers) {
return util.doCallback(callback, null, obj);
}
function dereferenceNextItem(err) {
if (err || keys.length === 0) {
// We're done! Invoke the callback
return util.doCallback(callback, err || null, obj);
function dereference(api, state, callback) {
if (!state.options.resolve$Refs) {
// Dereferencing is disabled, so just return the API as-is
util.doCallback(callback, null, api);
return;
}
var key = keys.pop();
var value = obj[key];
var fullPath = schemaPath + key;
// Resolve all $ref pointers
resolve(api, state, function(err, api) {
if (err) {
util.doCallback(callback, err);
return;
}
if (_.has(value, '$ref')) {
// We found a "$ref" pointer! So resolve it.
var pointerPath = fullPath + '/$ref';
var pointerValue = value.$ref;
// Replace all $ref pointers with the resolved values
util.crawlObject(api, callback,
function(parent, propName, propPath, continueCrawling) {
var $ref = parent[propName].$ref;
if (isExternalPointer(pointerValue) && !state.options.dereferenceExternalPointers) {
// This is an external pointer, and we're not resolving those, so just move along
dereferenceNextItem();
}
else {
resolvePointer(pointerPath, pointerValue, obj, key, state,
function(err, resolved, alreadyResolved) {
if (err || alreadyResolved) {
// The pointer had already been resolved, so no need to recurse over it
dereferenceNextItem(err);
if ($ref && _.has(state.$refs, $ref)) {
// We found a $ref pointer! So replace it.
parent[propName] = state.$refs[$ref];
}
// NOTE: This will also crawl the reference object that we just added,
// and replace any nested $ref pointers in it.
continueCrawling();
}
else {
// Recursively dereference the resolved reference
dereference(resolved, pointerPath, state, function(err) {
dereferenceNextItem(err);
});
}
}
);
}
}
else if (_.isPlainObject(value) || _.isArray(value)) {
// Recursively dereference each item in the object/array
dereference(value, fullPath, state, function(err, reference) {
obj[key] = reference;
dereferenceNextItem(err);
});
}
else {
// This is just a normal value (string, number, boolean, date, etc.)
// so just skip it and dereference the next item.
dereferenceNextItem();
}
}
schemaPath += '/';
// Loop through each item in the object/array
var keys = _.keys(obj);
dereferenceNextItem();
});
}
/**
* Resolves a "$ref" pointer.
* @param {string} pointerPath the path to the $ref property. This is only used for logging purposes.
* @param {string} pointerValue the pointer value to resolve
* @param {object} targetObj the object that will be updated to include the resolved reference
* @param {string} targetProp the property name on targetObj to be updated with the resolved reference
* @param {State} state the state for the current parse operation
* @param {function} callback
*/
function resolvePointer(pointerPath, pointerValue, targetObj, targetProp, state, callback) {
if (_.isEmpty(pointerValue)) {
return util.doCallback(callback, util.syntaxError('Empty $ref pointer at "%s"', pointerPath));
}
function returnResolvedValue(err, resolved, alreadyResolved) {
if (!err && resolved === undefined) {
err = util.syntaxError('Unable to resolve %s. The path "%s" could not be found in the Swagger file.',
pointerPath, pointerValue);
}
if (!err) {
// Update the target object with the resolved value
targetObj[targetProp] = resolved;
}
debug('Resolved %s => %s', pointerPath, pointerValue);
util.doCallback(callback, err, resolved, alreadyResolved);
}
try {
var cachedReference = getCachedReference(pointerValue, state);
if (cachedReference) {
returnResolvedValue(null, cachedReference, true);
}
else if (isExternalPointer(pointerValue)) {
resolveExternalPointer(pointerValue, state, returnResolvedValue);
}
else {
resolveInternalPointer(pointerValue, state, returnResolvedValue);
}
}
catch (e) {
util.doCallback(callback, e);
}
}
/**
* Returns the cached reference for the given pointer, if possible.
* @param {string} pointer The $ref pointer (e.g. "pet", "#/parameters/pet")
* @param {State} state the state for the current parse operation
*/
function getCachedReference(pointer, state) {
// Check for the non-normalized pointer
if (pointer in state.resolvedPointers) {
return state.resolvedPointers[pointer];
}
// Check for the normalized pointer
var normalized = normalizePointer(pointer, state);
if (normalized in state.resolvedPointers) {
// Cache the value under the non-normalized pointer too
return state.resolvedPointers[pointer] = state.resolvedPointers[normalized];
}
// It's not in the cache
return null;
}
/**
* Caches a resolved reference under the given pointer AND the normalized pointer.
* @param {string} pointer The $ref pointer (e.g. "pet", "#/definitions/pet")
* @param {string} normalized The normalized $ref pointer (e.g. "#/definitions/pet")
* @param {object} resolved The resolved reference
* @param {State} state the state for the current parse operation
*/
function cacheReference(pointer, normalized, resolved, state) {
state.resolvedPointers[pointer] = resolved;
state.resolvedPointers[normalized] = resolved;
}
/**
* Normalizes a pointer value.
* For example, "pet.yaml" and "./pet.yaml" both get normalized to "/swagger/base/dir/pet.yaml".
* @param {string} pointer The $ref pointer (e.g. "pet", "#/parameters/pet")
* @param {State} state the state for the current parse operation
* @returns {string}
*/
function normalizePointer(pointer, state) {
if (isExternalPointer(pointer)) {
// Normalize the pointer value by resolving the path/URL relative to the Swagger file.
return url.resolve(state.baseDir, pointer);
}
else {
if (pointer.indexOf('#/') === 0) {
// The pointer is already normalized (e.g. "#/parameters/username")
return pointer;
}
else {
// This is a shorthand pointer to a model definition
// "pet" => "#/definitions/pet"
return '#/definitions/' + pointer;
}
}
}
/**
* Determines whether the given $ref pointer value references an external file.
* @param {string} pointer The $ref pointer (e.g. "pet", "#/parameters/pet")
* @returns {boolean}
*/
function isExternalPointer(pointer) {
return pointer && externalPointerPattern.test(pointer);
}
/**
* Resolves a pointer to a property in the Swagger spec.
* @param {string} pointer The $ref pointer (e.g. "pet", "#/parameters/pet")
* @param {State} state the state for the current parse operation
* @param {function} callback
*/
function resolveInternalPointer(pointer, state, callback) {
var propertyPath;
// "pet" => "#/definitions/pet"
var normalized = normalizePointer(pointer, state);
// "#/paths/users/responses/200" => "paths.users.responses.200"
propertyPath = normalized.substr(2).replace(/\//g, '.');
// Get the property value from the schema
var resolved = resultDeep(state.swagger, propertyPath);
cacheReference(pointer, normalized, resolved, state);
callback(null, resolved);
}
/**
* Resolves a pointer to an external file or URL.
* @param {string} pointer the full, absolute path or URL
* @param {State} state the state for the current parse operation
* @param {function} callback
*/
function resolveExternalPointer(pointer, state, callback) {
// "./swagger.yaml" => "/full/path/to/swagger.yaml"
var normalized = normalizePointer(pointer, state);
// Set the resolved value to an empty object for now, so other reference pointers
// can point to this object. Once we finish reading the file, we will update
// the empty object with the real data.
var resolved = {};
cacheReference(pointer, normalized, resolved, state);
read.fileOrUrl(normalized, state,
function(err, data) {
if (!err) {
// Now that we've finished downloaded the data, update the empty object we created earlier
resolved = _.extend(resolved, data);
}
return callback(err, resolved);
}
);
}
/**
* Crawls the property tree to return the value of the specified property.
*/
function resultDeep(obj, key) {
// "my.deep.property" => ["my", "deep", "property"]
var propNames = key.split('.');
// Traverse each property/function
for (var i = 0; i < propNames.length; i++) {
var propName = propNames[i];
obj = _.result(obj, propName);
// Exit the loop early if we get a falsy value
if (!obj) {
break;
}
}
return obj;
}

@@ -33,66 +33,77 @@ 'use strict';

function parse(swaggerPath, options, callback) {
// Shift args if necessary
if (_.isFunction(options)) {
callback = options;
options = undefined;
}
// Shift args if necessary
if (_.isFunction(options)) {
callback = options;
options = undefined;
}
if (!_.isFunction(callback)) {
throw new Error('A callback function must be provided');
}
if (!_.isFunction(callback)) {
throw new Error('A callback function must be provided');
}
options = _.merge({}, defaults, options);
// Create a new state object for this parse operation
var state = new State();
state.options = _.merge({}, defaults, options);
// Resolve the file path or url, relative to the CWD
var cwd = util.cwd();
debug('Resolving Swagger file path "%s", relative to "%s"', cwd, swaggerPath);
swaggerPath = url.resolve(cwd, swaggerPath);
debug(' Resolved to %s', swaggerPath);
// Parse, dereference, and validate
parseSwaggerFile(swaggerPath, state, function(err, api) {
errBack(err) ||
dereference(api, state, function(err, api) {
errBack(err) ||
validateAgainstSchema(api, state, function(err, api) {
errBack(err) ||
finished(api, state);
});
});
});
// Create a new state object for this parse operation
var state = new State();
state.options = options;
state.baseDir = path.dirname(swaggerPath) + '/';
debug('Swagger base directory: %s', state.baseDir);
// Done!
function finished(api, state) {
var metadata = _.pick(state, 'baseDir', 'files', 'urls', '$refs');
util.doCallback(callback, null, api, metadata);
}
read.fileOrUrl(swaggerPath, state, function(err, swaggerObject) {
if (err) {
return util.doCallback(callback, err);
// Error!
function errBack(err) {
if (err) {
util.doCallback(callback, util.syntaxError(err, 'Error in "%s"', swaggerPath));
}
return !!err;
}
}
state.swagger = swaggerObject;
// Validate the version number
var version = swaggerObject.swagger;
if (supportedSwaggerVersions.indexOf(version) === -1) {
return util.doCallback(callback, util.syntaxError(
'Error in "%s". \nUnsupported Swagger version: %d. Swagger-Server only supports version %s',
swaggerPath, version, supportedSwaggerVersions.join(', ')));
}
/**
* Parses the given JSON or YAML file or URL.
* @param {string} filePath The absolute or relative file path
* @param {State} state The state for the current parse operation
* @param {function} callback
*/
function parseSwaggerFile(filePath, state, callback) {
// Resolve the file path or url, relative to the CWD
var cwd = util.cwd();
debug('Resolving Swagger file path "%s", relative to "%s"', filePath, cwd);
filePath = url.resolve(cwd, filePath);
debug(' Resolved to %s', filePath);
// Dereference the SwaggerObject by resolving "$ref" pointers
debug('Resolving $ref pointers in %s', swaggerPath);
dereference(swaggerObject, '', state, function(err, swaggerObject) {
if (!err) {
try {
// Validate the spec against the Swagger schema (if enabled)
if (state.options.validateSpec) {
validateAgainstSchema(swaggerObject, swaggerPath);
}
// Update the state
state.swaggerPath = filePath;
state.baseDir = path.dirname(filePath) + '/';
debug('Swagger base directory: %s', state.baseDir);
// Parse the file
read.fileOrUrl(filePath, state, function(err, api) {
if (err) {
util.doCallback(callback, err);
}
catch (e) {
err = e;
else if (supportedSwaggerVersions.indexOf(api.swagger) === -1) {
return util.doCallback(callback, util.syntaxError(
'Unsupported Swagger version: %d. Swagger-Parser only supports version %s',
api.swagger, supportedSwaggerVersions.join(', ')));
}
}
if (err) {
err = util.syntaxError(err, 'Error in "%s"', swaggerPath);
return util.doCallback(callback, err);
}
// We're done. Invoke the callback.
var metadata = _.omit(state, 'swagger', 'options');
util.doCallback(callback, null, swaggerObject, metadata);
else {
state.swagger = api;
util.doCallback(callback, null, api);
}
});
});
}

@@ -102,15 +113,26 @@

/**
* Validates the given SwaggerObject against the Swagger schema.
* Validates the given Swagger API against the Swagger schema.
* @param {SwaggerObject} api The absolute or relative file path
* @param {State} state The state for the current parse operation
* @param {function} callback
*/
function validateAgainstSchema(swaggerObject, swaggerPath) {
debug('Validating "%s" against the Swagger schema', swaggerPath);
if (tv4.validate(swaggerObject, swaggerSchema)) {
debug(' Validated successfully');
return true;
}
else {
throw util.syntaxError('%s \nData path: "%s" \nSchema path: "%s"\n',
tv4.error.message, tv4.error.dataPath, tv4.error.schemaPath);
}
function validateAgainstSchema(api, state, callback) {
if (state.options.validateSchema) {
debug('Validating "%s" against the Swagger schema', state.swaggerPath);
if (tv4.validate(api, swaggerSchema)) {
debug(' Validated successfully');
util.doCallback(callback, null, api);
}
else {
util.doCallback(callback, util.syntaxError(
'%s \nData path: "%s" \nSchema path: "%s"\n',
tv4.error.message, tv4.error.dataPath, tv4.error.schemaPath));
}
}
else {
// Schema validation is disabled, so just return the API as-is
util.doCallback(callback, null, api);
}
}

@@ -14,23 +14,23 @@ 'use strict';

module.exports = read = {
/**
* Reads a JSON or YAML file from the local filesystem or a remote URL and returns the parsed POJO.
* @param {string} pathOrUrl A full, absolute file path or URL
* @param {State} state The state for the current parse operation
* @param {function} callback function(err, parsedObject)
*/
fileOrUrl: function(pathOrUrl, state, callback) {
try {
// Determine whether its a local file or a URL
var parsedUrl = url.parse(pathOrUrl);
if (isLocalFile(parsedUrl)) {
readFile(parsedUrl.href, state, callback);
}
else {
readUrl(parsedUrl, state, callback);
}
/**
* Reads a JSON or YAML file from the local filesystem or a remote URL and returns the parsed POJO.
* @param {string} pathOrUrl A full, absolute file path or URL
* @param {State} state The state for the current parse operation
* @param {function} callback function(err, parsedObject)
*/
fileOrUrl: function(pathOrUrl, state, callback) {
try {
// Determine whether its a local file or a URL
var parsedUrl = url.parse(pathOrUrl);
if (isLocalFile(parsedUrl)) {
readFile(parsedUrl.href, state, callback);
}
else {
readUrl(parsedUrl, state, callback);
}
}
catch (e) {
callback(e);
}
}
catch (e) {
callback(e);
}
}
};

@@ -46,30 +46,30 @@

function readFile(filePath, state, callback) {
function errorHandler(err) {
callback(util.error(err, 'Error opening file "%s"', filePath));
}
function errorHandler(err) {
callback(util.error(err, 'Error opening file "%s"', filePath));
}
function parseError(err) {
callback(util.syntaxError(err, 'Error parsing file "%s"', filePath));
}
function parseError(err) {
callback(util.syntaxError(err, 'Error parsing file "%s"', filePath));
}
try {
debug('Reading file "%s"', filePath);
try {
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);
}
}

@@ -85,71 +85,71 @@

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);
// 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 downloadError(err) {
callback(util.error(err, 'Error downloading file "%s"', parsedUrl.href));
}
function downloadError(err) {
callback(util.error(err, 'Error downloading file "%s"', parsedUrl.href));
}
function parseError(err) {
callback(util.syntaxError(err, 'Error parsing file "%s"', parsedUrl.href));
}
function parseError(err) {
callback(util.syntaxError(err, 'Error parsing file "%s"', parsedUrl.href));
}
try {
var options = {
host: parsedUrl.host,
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.path,
auth: parsedUrl.auth,
headers: {'Content-Type': 'application/json'}
};
try {
var options = {
host: parsedUrl.host,
hostname: parsedUrl.hostname,
port: parsedUrl.port,
path: parsedUrl.path,
auth: parsedUrl.auth,
headers: {'Content-Type': 'application/json'}
};
debug('Downloading file "%s"', parsedUrl.href);
debug('Downloading file "%s"', parsedUrl.href);
var req = http.get(options, function(res) {
var body = '';
var req = http.get(options, function(res) {
var body = '';
if (_.isFunction(res.setEncoding)) {
res.setEncoding('utf8');
}
if (_.isFunction(res.setEncoding)) {
res.setEncoding('utf8');
}
res.on('data', function(data) {
body += data;
});
res.on('data', function(data) {
body += data;
});
res.on('end', function() {
if (res.statusCode >= 400) {
return downloadError(new Error('HTTP ERROR ' + res.statusCode + ': ' + body));
}
res.on('end', function() {
if (res.statusCode >= 400) {
return downloadError(new Error('HTTP ERROR ' + res.statusCode + ': ' + body));
}
try {
state.urls.push(parsedUrl);
callback(null, parseJsonOrYaml(parsedUrl.href, body, state));
try {
state.urls.push(parsedUrl);
callback(null, parseJsonOrYaml(parsedUrl.href, body, state));
}
catch (e) {
parseError(e);
}
});
res.on('error', function(e) {
downloadError(e);
});
});
if (_.isFunction(req.setTimeout)) {
req.setTimeout(5000);
}
catch (e) {
parseError(e);
}
});
res.on('error', function(e) {
downloadError(e);
});
});
req.on('timeout', function() {
req.abort();
});
if (_.isFunction(req.setTimeout)) {
req.setTimeout(5000);
req.on('error', function(e) {
downloadError(e);
});
}
req.on('timeout', function() {
req.abort();
});
req.on('error', function(e) {
downloadError(e);
});
}
catch (e) {
downloadError(e);
}
catch (e) {
downloadError(e);
}
}

@@ -164,15 +164,15 @@

function isLocalFile(parsedUrl) {
if (util.isBrowser()) {
// Local files aren't supported in browsers
return false;
}
if (util.isBrowser()) {
// Local files aren't supported in browsers
return false;
}
// If the path exists locally, then treat the URL as a local file
if (fs.existsSync(parsedUrl.pathname)) {
return true;
}
// If the path exists locally, then treat the URL as a local file
if (fs.existsSync(parsedUrl.pathname)) {
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:';
// 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:';
}

@@ -189,22 +189,22 @@

function parseJsonOrYaml(pathOrUrl, data, state) {
var parsedObject;
if (state.options.parseYaml) {
debug('Parsing YAML file "%s"', pathOrUrl);
parsedObject = yaml.safeLoad(data);
}
else {
debug('Parsing JSON file "%s"', pathOrUrl);
parsedObject = JSON.parse(data);
}
var parsedObject;
if (state.options.parseYaml) {
debug('Parsing YAML file "%s"', pathOrUrl);
parsedObject = yaml.safeLoad(data);
}
else {
debug('Parsing JSON file "%s"', pathOrUrl);
parsedObject = JSON.parse(data);
}
if (_.isEmpty(parsedObject)) {
throw util.syntaxError('Parsed value is empty');
}
if (!_.isPlainObject(parsedObject)) {
throw util.syntaxError('Parsed value is not a valid JavaScript object');
}
if (_.isEmpty(parsedObject)) {
throw util.syntaxError('Parsed value is empty');
}
if (!_.isPlainObject(parsedObject)) {
throw util.syntaxError('Parsed value is not a valid JavaScript object');
}
debug(' Parsed successfully');
return parsedObject;
debug(' Parsed successfully');
return parsedObject;
}

@@ -10,36 +10,48 @@ 'use strict';

function State() {
/**
* The directory of the Swagger file
* (used as the base directory for resolving relative file references)
*/
this.baseDir = null;
/**
* The path of the main Swagger file that's being parsed
* @type {string}
*/
this.swaggerPath = '';
/**
* The options for the parsing operation
* @type {defaults}
*/
this.options = null;
/**
* The options for the parsing operation
* @type {defaults}
*/
this.options = null;
/**
* The Swagger object that is being parsed.
*/
this.swagger = null;
/**
* The Swagger API that is being parsed.
* @type {SwaggerObject}
*/
this.swagger = null;
/**
* The files that have been read during the parsing operation.
* @type {string[]}
*/
this.files = [];
/**
* The directory of the Swagger file
* (used as the base directory for resolving relative file references)
*/
this.baseDir = null;
/**
* The URLs that have been downloaded during the parsing operation.
* @type {url.Url[]}
*/
this.urls = [];
/**
* The files that have been read during the parsing operation.
* @type {string[]}
*/
this.files = [];
/**
* A map of resolved "$ref" pointers and values
*/
this.resolvedPointers = {};
/**
* The URLs that have been downloaded during the parsing operation.
* @type {url.Url[]}
*/
this.urls = [];
/**
* A map of "$ref" pointers and their resolved values
*/
this.$refs = {};
}
/**
* The Swagger object (https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object-)
* @typedef {{swagger: string, info: {}, paths: {}}} SwaggerObject
*/

@@ -10,117 +10,190 @@ 'use strict';

var util = module.exports = {
/**
* Asynchronously invokes the given callback function with the given parameters.
* This allows the call stack to unwind, which is necessary because there can be a LOT of
* recursive calls when dereferencing large Swagger specs.
* @param {function} callback
* @param {*} [err]
* @param {...*} [params]
*/
doCallback: function(callback, err, params) {
var args = _.rest(arguments);
process.nextTick(function() {
callback.apply(null, args);
});
},
/**
* Asynchronously invokes the given callback function with the given parameters.
*
* @param {function} callback
* @param {*} [err]
* @param {...*} [params]
*/
doCallback: function(callback, err, params) {
var args = _.rest(arguments);
process.nextTick(function() {
callback.apply(null, args);
});
},
/**
* Creates an Error with a formatted string message.
* @param {Error} [err] The original error, if any
* @param {string} [message] A user-friendly message about the source of the error
* @param {...*} [params] One or more {@link util#format} params
* @returns {Error}
*/
error: function(err, message, params) {
if (err && err instanceof Error) {
return new Error(errorDump.apply(null, arguments));
}
else {
return new Error(format.apply(null, arguments));
}
},
/**
* Recursively crawls an object, and calls the given function for each nested object.
*
* @param {object|*[]} obj
* The object (or array) to be crawled
*
* @param {string} [path]
* The starting path of the object (e.g. "/definitions/pet")
*
* @param {function} callback
* Called when the entire object tree is done being crawled, or when an error occurs.
* The signature is `function(err, obj)`.
*
* @param {function} forEach
* Called for each nested object in the tree. The signature is `function(parent, propName, propPath, continue)`,
* where `parent` is the parent object, `propName` is the name of the nested object property,
* `propPath` is a full path of nested object (e.g. "/paths//get/responses/200/schema"),
* and `continue` is a function to call to resume crawling the object.
*/
crawlObject: function(obj, path, callback, forEach) {
// Shift args if needed
if (_.isFunction(path)) {
forEach = callback;
callback = path;
path = '';
}
// Loop through each item in the object/array
var properties = _.keys(obj);
crawlNextProperty();
/**
* Creates a SyntaxError with a formatted string message.
* @param {Error} [err] The original error, if any
* @param {string} [message] A user-friendly message about the source of the error
* @param {...*} [params] One or more {@link util#format} params
* @returns {SyntaxError}
*/
syntaxError: function(err, message, params) {
if (err && err instanceof Error) {
return new SyntaxError(errorDump.apply(null, arguments));
}
else {
return new SyntaxError(format.apply(null, arguments));
}
},
function crawlNextProperty(err) {
if (err) {
// An error occurred, so stop crawling and bubble it up
util.doCallback(callback, err);
return;
}
else if (properties.length === 0) {
// We've crawled all of this object's properties, so we're done.
util.doCallback(callback, null, obj);
return;
}
var propName = properties.pop();
var propValue = obj[propName];
var propPath = path + '/' + propName;
/**
* Determines if we're running in a browser.
* @returns {boolean}
*/
isBrowser: function() {
return fs.readFile === undefined;
},
if (_.isPlainObject(propValue)) {
// Found an object property, so call the callback
util.doCallback(forEach, obj, propName, propPath, function(err) {
if (err) {
// An error occurred, so bubble it up
crawlNextProperty(err);
}
else {
// Crawl the nested object (re-fetch it from the parent obj, in case it has changed)
util.crawlObject(obj[propName], propPath, crawlNextProperty, forEach);
}
});
}
else if (_.isArray(propValue)) {
// This is an array property, so crawl its items
util.crawlObject(propValue, propPath, crawlNextProperty, forEach);
}
else {
// This isn't an object property, so skip it
crawlNextProperty();
}
}
},
/**
* Normalizes the current working directory across environments (Linux, Mac, Windows, browsers).
* The returned path will use forward slashes ("/"), even on Windows,
* and will always include a trailing slash, even at the root of a website (e.g. "http://google.com/")
* @returns {string}
*/
cwd: function() {
var path = util.isBrowser() ? window.location.href : process.cwd() + '/';
/**
* Creates an Error with a formatted string message.
*
* @param {Error} [err] The original error, if any
* @param {string} [message] A user-friendly message about the source of the error
* @param {...*} [params] One or more {@link util#format} params
* @returns {Error}
*/
error: function(err, message, params) {
if (err && err instanceof Error) {
return new Error(errorDump.apply(null, arguments));
}
else {
return new Error(format.apply(null, arguments));
}
},
// Parse the path as a URL, which normalizes it across all platforms
var parsedUrl = url.parse(path);
// Remove the file name (if any) from the pathname
var lastSlash = parsedUrl.pathname.lastIndexOf('/') + 1;
parsedUrl.pathname = parsedUrl.pathname.substr(0, lastSlash);
/**
* Creates a SyntaxError with a formatted string message.
*
* @param {Error} [err] The original error, if any
* @param {string} [message] A user-friendly message about the source of the error
* @param {...*} [params] One or more {@link util#format} params
* @returns {SyntaxError}
*/
syntaxError: function(err, message, params) {
if (err && err instanceof Error) {
return new SyntaxError(errorDump.apply(null, arguments));
}
else {
return new SyntaxError(format.apply(null, arguments));
}
},
// Remove everything after the pathname
parsedUrl.path = null;
parsedUrl.search = null;
parsedUrl.query = null;
parsedUrl.hash = null;
// Now re-parse the URL with only the remaining parts
return url.format(parsedUrl);
}
/**
* Determines if we're running in a browser.
* @returns {boolean}
*/
isBrowser: function() {
return fs.readFile === undefined;
},
/**
* Normalizes the current working directory across environments (Linux, Mac, Windows, browsers).
* The returned path will use forward slashes ("/"), even on Windows,
* and will always include a trailing slash, even at the root of a website (e.g. "http://google.com/")
* @returns {string}
*/
cwd: function() {
var path = util.isBrowser() ? window.location.href : process.cwd() + '/';
// Parse the path as a URL, which normalizes it across all platforms
var parsedUrl = url.parse(path);
// Remove the file name (if any) from the pathname
var lastSlash = parsedUrl.pathname.lastIndexOf('/') + 1;
parsedUrl.pathname = parsedUrl.pathname.substr(0, lastSlash);
// Remove everything after the pathname
parsedUrl.path = null;
parsedUrl.search = null;
parsedUrl.query = null;
parsedUrl.hash = null;
// Now re-parse the URL with only the remaining parts
return url.format(parsedUrl);
}
};
/**
* Returns detailed error information that can be written to a log, stderror, etc.
* @param {Error} err The Error object
* @param {string} [message] Optional message about where and why the error occurred.
* @param {...*} [params] One or more params to be passed to {@link util#format}
*/
* Returns detailed error information that can be written to a log, stderror, etc.
*
* @param {Error} err The Error object
* @param {string} [message] Optional message about where and why the error occurred.
* @param {...*} [params] One or more params to be passed to {@link util#format}
*/
function errorDump(err, message, params) {
// Format the message string
message = format.apply(null, _.rest(arguments)) + ': \n';
// Format the message string
message = format.apply(null, _.rest(arguments)) + ': \n';
// Gather detailed error information
message += (err.name || 'Error') + ': ';
// Gather detailed error information
message += (err.name || 'Error') + ': ';
var stack = err.stack;
var stack = err.stack;
/* istanbul ignore else: Only IE doesn't have an Error.stack property */
if (stack) {
/* istanbul ignore if: Only Safari doesn't include Error.message in Error.stack */
if (stack.indexOf(err.message) === -1) {
message += err.message + ' \n';
/* istanbul ignore else: Only IE doesn't have an Error.stack property */
if (stack) {
/* istanbul ignore if: Only Safari doesn't include Error.message in Error.stack */
if (stack.indexOf(err.message) === -1) {
message += err.message + ' \n';
}
return message + stack;
}
return message + stack;
}
else {
return message + err.message;
}
else {
return message + err.message;
}
}
{
"name": "swagger-parser",
"version": "1.0.11",
"version": "2.0.0",
"description": "Swagger JSON/YAML parser and validator for Node and browsers",

@@ -45,3 +45,3 @@ "keywords": [

"devDependencies": {
"browserify": "^7.0.0",
"browserify": "^8.0.0",
"chai": "^1.10.0",

@@ -67,3 +67,3 @@ "coveralls": "^2.11.2",

"mocha": "^2.0.1",
"nock": "^0.51.0",
"nock": "^0.52.0",
"sinon": "^1.12.1",

@@ -70,0 +70,0 @@ "vinyl-buffer": "^1.0.0",

@@ -6,3 +6,3 @@ Swagger-Parser

[![Build Status](https://img.shields.io/travis/BigstickCarpet/swagger-parser.svg)](https://travis-ci.org/BigstickCarpet/swagger-parser)
[![Dependencies](https://david-dm.org/bigstickcarpet/swagger-parser.svg)](https://david-dm.org/bigstickcarpet/swagger-parser)
[![Dependencies](https://img.shields.io/david/bigstickcarpet/swagger-parser.svg)](https://david-dm.org/bigstickcarpet/swagger-parser)
[![Code Climate Score](https://codeclimate.com/github/BigstickCarpet/swagger-parser/badges/gpa.svg)](https://codeclimate.com/github/BigstickCarpet/swagger-parser)

@@ -36,3 +36,3 @@ [![Codacy Score](http://img.shields.io/codacy/6d686f916836433b9c013379fbe1052c.svg)](https://www.codacy.com/public/jamesmessinger/swagger-parser)

````
The `api` parameter that's passed to the callback function is the parsed, validated, and dereferenced [Swagger Object](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object-).
The `api` parameter that's passed to the callback function is the parsed, validated, and dereferenced [Swagger object](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object-).

@@ -80,57 +80,38 @@

--------------------------
### `Parser.parse()`
### `Parser.parse(swaggerPath, [options], callback)`
This method can be called with two parameters (as shown above), or with three parameters, like this:
* __`swaggerPath`__ (_required_) - string<br>
The file path or URL of your Swagger file. Relative paths are allowed. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page.
````javascript
var options = {
dereferencePointers: false,
validateSpec: false
};
parser.parse("swagger.yaml", options, function(err, api, metadata) {
...
});
````
The three parameters are as follows:
* __`options`__ (_optional_) - object<br>
An object containing one or more parsing options. See [options](#options) below.
* __`callback`__ (_required_) - function(err, api, metadata)<br>
Called after the parsing is finished, or when an error occurs. See [callback](#callback) below for details.
### `swaggerFile` - string (_required_)
The file path or URL of the Swagger file. Relative paths are allowed. In Node, the path is relative to `process.cwd()`. In the browser, it's relative to the URL of the page.
#### Options
|Property |Type |Default |Description
|:----------------------|:-----------|:-------------|:----------
|`parseYaml` |bool |true |Determines whether the parser will allow Swagger specs in YAML format. If set to `false`, then only JSON will be allowed.
|`resolve$Refs` |bool |true |Determines whether `$ref` pointers will be resolved. If set to `false`, then the resulting Swagger object will contain [Reference objects](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-) instead of the objects they reference.
|`resolveExternal$Refs` |bool |true |Determines whether `$ref` pointers will be resolved if they point to external files or URLs. If set to `false` then the resulting Swagger object will contain [Reference objects](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-) for external pointers instead of the objects they reference.
|`validateSchema` |bool |true |Determines whether your API will be validated against the official Swagger schema. If set to `false`, then the resulting [Swagger object](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object-) may be missing properties, have properties of the wrong data type, etc.
### `options` - object (_optional_)
An object containing one or more of the following properties:
#### Callback
|Parameter |Type |Description
|:----------|:-------------------|:----------
|`err` |Error |`null` unless an error occurred.
|`api` |[Swagger object](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#swagger-object-) |The complete Swagger API object. Or `undefined` if an error occurred
|`metadata` |object |This parameter can usually be ignored. It provides useful information about the parsing operation itself.
* __parseYaml__ (default: true) -
Determines whether the parser will allow Swagger specs in YAML format. If set to `false`, then only JSON will be allowed.
The `metadata` parameter is an object with the following properties:
* __dereferencePointers__ (default: true) -
Determines whether `$ref` pointers will be dereferenced. If set to `false`, then the resulting SwaggerObject will contain [Reference Objects](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-) instead of the objects they reference.
|Property |Type |Description
|:----------|:-------------------|:----------
|`baseDir` |string |The directory of the main Swagger file, which is the base directory used to resolve any external $ref pointers.
|`files` |array of strings |The full paths of all files that were parsed. This only includes local files, _not_ URLs. If `Parser.parse()` was called with a local file path, then it will be the first item in this array.
|`urls` |array of [URL objects](http://nodejs.org/api/url.html#url_url)|The URLs that were parsed. If `Parser.parse()` was called with a URL, then it will be the first item in this array.
|`$refs` |object |A map of all the $ref pointers that were resolved, and the objects they resolved to.
* __dereferenceExternalPointers__ (default: true) -
Determines whether `$ref` pointers will be dereferenced if they point to external files (e.g. "http://company.com/my/schema.yaml"). If set to `false` then the resulting SwaggerObject will contain [Reference Objects](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#reference-object-) for external pointers instead of the objects they reference.
* __validateSpec__ (default: true) -
Determines whether your Swagger spec will be validated against the official Swagger schema. If set to `false`, then the resulting [Swagger Object](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object-) may be missing properties, have properties of the wrong data type, etc.
### `callback` - function (_required_)
The callback function is called when the Swagger file and all referenced files have been downloaded, parsed, validated, and dereferenced.
* __err__ (Error object) -
If an error occurred during parsing, then this will be the `Error` object; otherwise, this parameter will be `null`. You should always check the `err` parameter first, because the other parameters may be `undefined` if an error occurred.
* __api__ (Swagger object) -
If the file(s) were parsed successfully, then this object is the complete Swagger API object. See the [official Swagger 2.0 specification](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#swagger-object-) for details.
* __metadata__ (object) -
This object provides useful information about the parsing operation itself, namely:
* __baseDir__ (string) - The directory of the main Swagger file, which is the base directory used to resolve any relative $ref pointers.
* __files__ (array of strings) - The full paths of all files that were parsed. This only includes local files, _not_ URLs. If The main Swagger file was local, then it will be the first item in this array.
* __urls__ (array of objects) - The URLs that were parsed. Each item in the array is a [URL object](http://nodejs.org/api/url.html#url_url), which lets you easily access the full URL, or specific parts of it.
* __resolvedPointers__ (object) - A map of all the $ref pointers that were resolved, and the objects they resolved to.
Contributing

@@ -137,0 +118,0 @@ --------------------------

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc