Skip to content

Commit

Permalink
Merge pull request #225 from kibertoad/fix/ts-and-imports
Browse files Browse the repository at this point in the history
Fix support for newer TypeScript and named imports
  • Loading branch information
philsturgeon authored Oct 19, 2022
2 parents 534bb3f + be3f009 commit 803ff45
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 167 deletions.
303 changes: 146 additions & 157 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,191 +11,180 @@ const { ono } = require("@jsdevtools/ono");
const $RefParser = require("@apidevtools/json-schema-ref-parser");
const dereference = require("@apidevtools/json-schema-ref-parser/lib/dereference");

module.exports = SwaggerParser;

/**
* This class parses a Swagger 2.0 or 3.0 API, resolves its JSON references and their resolved values,
* and provides methods for traversing, dereferencing, and validating the API.
*
* @class
* @augments $RefParser
*/
function SwaggerParser () {
$RefParser.apply(this, arguments);
}

util.inherits(SwaggerParser, $RefParser);
SwaggerParser.parse = $RefParser.parse;
SwaggerParser.resolve = $RefParser.resolve;
SwaggerParser.bundle = $RefParser.bundle;
SwaggerParser.dereference = $RefParser.dereference;

/**
* Alias {@link $RefParser#schema} as {@link SwaggerParser#api}
*/
Object.defineProperty(SwaggerParser.prototype, "api", {
configurable: true,
enumerable: true,
get () {
return this.schema;
}
});

/**
* Parses the given Swagger API.
* This method does not resolve any JSON references.
* It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
*
* @param {string} [path] - The file path or URL of the JSON schema
* @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`.
* @param {ParserOptions} [options] - Options that determine how the API is parsed
* @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object.
* @returns {Promise} - The returned promise resolves with the parsed API object.
*/
SwaggerParser.prototype.parse = async function (path, api, options, callback) {
let args = normalizeArgs(arguments);
args.options = new Options(args.options);

try {
let schema = await $RefParser.prototype.parse.call(this, args.path, args.schema, args.options);

if (schema.swagger) {
// Verify that the parsed object is a Swagger API
if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) {
throw ono.syntax(`${args.path || args.schema} is not a valid Swagger API definition`);
}
else if (typeof schema.swagger === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.');
}
else if (typeof schema.info.version === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.');
}
else if (schema.swagger !== "2.0") {
throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`);
class SwaggerParser extends $RefParser {

/**
* Parses the given Swagger API.
* This method does not resolve any JSON references.
* It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
*
* @param {string} [path] - The file path or URL of the JSON schema
* @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`.
* @param {ParserOptions} [options] - Options that determine how the API is parsed
* @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object.
* @returns {Promise} - The returned promise resolves with the parsed API object.
*/
async parse (path, api, options, callback) {
let args = normalizeArgs(arguments);
args.options = new Options(args.options);

try {
let schema = await super.parse(args.path, args.schema, args.options);

if (schema.swagger) {
// Verify that the parsed object is a Swagger API
if (schema.swagger === undefined || schema.info === undefined || schema.paths === undefined) {
throw ono.syntax(`${args.path || args.schema} is not a valid Swagger API definition`);
}
else if (typeof schema.swagger === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('Swagger version number must be a string (e.g. "2.0") not a number.');
}
else if (typeof schema.info.version === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.');
}
else if (schema.swagger !== "2.0") {
throw ono.syntax(`Unrecognized Swagger version: ${schema.swagger}. Expected 2.0`);
}
}
}
else {
let supportedVersions = ["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0"];
else {
let supportedVersions = ["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0"];

// Verify that the parsed object is a Openapi API
if (schema.openapi === undefined || schema.info === undefined) {
throw ono.syntax(`${args.path || args.schema} is not a valid Openapi API definition`);
}
else if (schema.paths === undefined) {
if (schema.openapi === "3.1.0") {
if (schema.webhooks === undefined) {
// Verify that the parsed object is a Openapi API
if (schema.openapi === undefined || schema.info === undefined) {
throw ono.syntax(`${args.path || args.schema} is not a valid Openapi API definition`);
}
else if (schema.paths === undefined) {
if (schema.openapi === "3.1.0") {
if (schema.webhooks === undefined) {
throw ono.syntax(`${args.path || args.schema} is not a valid Openapi API definition`);
}
}
else {
throw ono.syntax(`${args.path || args.schema} is not a valid Openapi API definition`);
}
}
else {
throw ono.syntax(`${args.path || args.schema} is not a valid Openapi API definition`);
else if (typeof schema.openapi === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('Openapi version number must be a string (e.g. "3.0.0") not a number.');
}
}
else if (typeof schema.openapi === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('Openapi version number must be a string (e.g. "3.0.0") not a number.');
}
else if (typeof schema.info.version === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.');
}
else if (supportedVersions.indexOf(schema.openapi) === -1) {
throw ono.syntax(
`Unsupported OpenAPI version: ${schema.openapi}. ` +
`Swagger Parser only supports versions ${supportedVersions.join(", ")}`
);
else if (typeof schema.info.version === "number") {
// This is a very common mistake, so give a helpful error message
throw ono.syntax('API version number must be a string (e.g. "1.0.0") not a number.');
}
else if (supportedVersions.indexOf(schema.openapi) === -1) {
throw ono.syntax(
`Unsupported OpenAPI version: ${schema.openapi}. ` +
`Swagger Parser only supports versions ${supportedVersions.join(", ")}`
);
}

// This is an OpenAPI v3 schema, check if the "servers" have any relative paths and
// fix them if the content was pulled from a web resource
util.fixOasRelativeServers(schema, args.path);
}

// This is an OpenAPI v3 schema, check if the "servers" have any relative paths and
// fix them if the content was pulled from a web resource
util.fixOasRelativeServers(schema, args.path);
// Looks good!
return maybe(args.callback, Promise.resolve(schema));
}
catch (err) {
return maybe(args.callback, Promise.reject(err));
}

// Looks good!
return maybe(args.callback, Promise.resolve(schema));
}
catch (err) {
return maybe(args.callback, Promise.reject(err));
}
};

/**
* Parses, dereferences, and validates the given Swagger API.
* Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation.
*
* @param {string} [path] - The file path or URL of the JSON schema
* @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`.
* @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated
* @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object.
* @returns {Promise} - The returned promise resolves with the parsed API object.
*/
SwaggerParser.validate = function (path, api, options, callback) {
let Class = this; // eslint-disable-line consistent-this
let instance = new Class();
return instance.validate.apply(instance, arguments);
};

/**
* Parses, dereferences, and validates the given Swagger API.
* Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation.
*
* @param {string} [path] - The file path or URL of the JSON schema
* @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`.
* @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated
* @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object.
* @returns {Promise} - The returned promise resolves with the parsed API object.
*/
SwaggerParser.prototype.validate = async function (path, api, options, callback) {
let me = this;
let args = normalizeArgs(arguments);
args.options = new Options(args.options);

// ZSchema doesn't support circular objects, so don't dereference circular $refs yet
// (see https://github.com/zaggino/z-schema/issues/137)
let circular$RefOption = args.options.dereference.circular;
args.options.validate.schema && (args.options.dereference.circular = "ignore");

try {
await this.dereference(args.path, args.schema, args.options);

// Restore the original options, now that we're done dereferencing
args.options.dereference.circular = circular$RefOption;

if (args.options.validate.schema) {
// Validate the API against the Swagger schema
// NOTE: This is safe to do, because we haven't dereferenced circular $refs yet
validateSchema(me.api);

if (me.$refs.circular) {
if (circular$RefOption === true) {
// The API has circular references,
// so we need to do a second-pass to fully-dereference it
dereference(me, args.options);
}
else if (circular$RefOption === false) {
// The API has circular references, and they're not allowed, so throw an error
throw ono.reference("The API contains circular references");
/**
* Parses, dereferences, and validates the given Swagger API.
* Depending on the options, validation can include JSON Schema validation and/or Swagger Spec validation.
*
* @param {string} [path] - The file path or URL of the JSON schema
* @param {object} [api] - The Swagger API object. This object will be used instead of reading from `path`.
* @param {ParserOptions} [options] - Options that determine how the API is parsed, dereferenced, and validated
* @param {Function} [callback] - An error-first callback. The second parameter is the parsed API object.
* @returns {Promise} - The returned promise resolves with the parsed API object.
*/
async validate (path, api, options, callback) {
let me = this;
let args = normalizeArgs(arguments);
args.options = new Options(args.options);

// ZSchema doesn't support circular objects, so don't dereference circular $refs yet
// (see https://github.com/zaggino/z-schema/issues/137)
let circular$RefOption = args.options.dereference.circular;
args.options.validate.schema && (args.options.dereference.circular = "ignore");

try {
await this.dereference(args.path, args.schema, args.options);

// Restore the original options, now that we're done dereferencing
args.options.dereference.circular = circular$RefOption;

if (args.options.validate.schema) {
// Validate the API against the Swagger schema
// NOTE: This is safe to do, because we haven't dereferenced circular $refs yet
validateSchema(me.api);

if (me.$refs.circular) {
if (circular$RefOption === true) {
// The API has circular references,
// so we need to do a second-pass to fully-dereference it
dereference(me, args.options);
}
else if (circular$RefOption === false) {
// The API has circular references, and they're not allowed, so throw an error
throw ono.reference("The API contains circular references");
}
}
}
}

if (args.options.validate.spec) {
// Validate the API against the Swagger spec
validateSpec(me.api);
}
if (args.options.validate.spec) {
// Validate the API against the Swagger spec
validateSpec(me.api);
}

return maybe(args.callback, Promise.resolve(me.schema));
return maybe(args.callback, Promise.resolve(me.schema));
}
catch (err) {
return maybe(args.callback, Promise.reject(err));
}
}
catch (err) {
return maybe(args.callback, Promise.reject(err));
}


/**
* Alias {@link $RefParser#schema} as {@link SwaggerParser#api}
*/
Object.defineProperty(SwaggerParser.prototype, "api", {
configurable: true,
enumerable: true,
get () {
return this.schema;
}
};
});

/**
* The Swagger object
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#swagger-object
*
* @typedef {{swagger: string, info: {}, paths: {}}} SwaggerObject
*/

const defaultInstance = new SwaggerParser();
const defaultExport = SwaggerParser;

defaultExport.validate = (...args) => { return defaultInstance.validate(...args); };
defaultExport.dereference = (...args) => { return defaultInstance.dereference(...args); };
defaultExport.bundle = (...args) => { return defaultInstance.bundle(...args); };
defaultExport.parse = (...args) => { return defaultInstance.parse(...args); };
defaultExport.resolve = (...args) => { return defaultInstance.resolve(...args); };
defaultExport.default = defaultExport;
defaultExport.SwaggerParser = defaultExport;

module.exports = defaultExport;
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@types/node": "^14.6.4",
"chai": "^4.2.0",
"eslint": "^7.8.1",
"js-yaml": "^3.14.0",
"js-yaml": "^3.14.1",
"karma": "^6.3.19",
"karma-cli": "^2.0.0",
"mocha": "^8.1.3",
Expand All @@ -73,7 +73,7 @@
"shx": "^0.3.2",
"simplifyify": "^7.0.7",
"sinon": "^11.1.2",
"typescript": "^4.0.2"
"typescript": "^4.7.4"
},
"dependencies": {
"@apidevtools/json-schema-ref-parser": "9.0.6",
Expand Down
Loading

0 comments on commit 803ff45

Please sign in to comment.