-
Notifications
You must be signed in to change notification settings - Fork 167
Expand file tree
/
Copy pathindex.js
More file actions
197 lines (176 loc) · 8.1 KB
/
index.js
File metadata and controls
197 lines (176 loc) · 8.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* eslint-disable no-unused-vars */
"use strict";
const validateSchema = require("./validators/schema");
const validateSpec = require("./validators/spec");
const normalizeArgs = require("@apidevtools/json-schema-ref-parser/lib/normalize-args");
const util = require("./util");
const ParserOptions = require("./options");
const maybe = require("call-me-maybe");
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 ParserOptions(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`);
}
}
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) {
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.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);
}
// 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 ParserOptions(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; hand in $refs.circular meta-data from the SwaggerParser
validateSpec(me.api, me.$refs.circular);
}
return maybe(args.callback, Promise.resolve(me.schema));
}
catch (err) {
return maybe(args.callback, Promise.reject(err));
}
};
// Only the one export; don't try to export the @typedef of SwaggerOrOpenAPIObject
module.exports = SwaggerParser;