function createMiddleware(swagger, router, callback) { // Shift args if needed if (util.isExpressRouter(swagger)) { router = swagger; swagger = callback = undefined; } else if (!util.isExpressRouter(router)) { callback = router; router = undefined; } var middleware = new module.exports.Middleware(router); if (swagger) { middleware.init(swagger, callback); } return middleware; }
n/a
function DataStore() {
/**
* The Express Application or Router that's used to determine case-sensitivity and/or strict matching
* of collection paths and resource names.
*
* @type {express#Router}
* @protected
*/
this.__router = {};
}
n/a
function FileDataStore(baseDir) { DataStore.call(this); this.__baseDir = baseDir || process.cwd(); }
n/a
function MemoryDataStore() {
DataStore.call(this);
/**
* This implementation of DataStore uses an in-memory array.
* @type {Resource[]}
* @private
*/
this.__resourceStore = [];
}
n/a
function Middleware(sharedRouter) {
sharedRouter = util.isExpressRouter(sharedRouter) ? sharedRouter : undefined;
var self = this;
var context = new MiddlewareContext(sharedRouter);
/**
* Initializes the middleware with the given Swagger API.
* This method can be called again to re-initialize with a new or modified API.
*
* @param {string|object} [swagger]
* - The file path or URL of a Swagger 2.0 API spec, in YAML or JSON format.
* Or a valid Swagger API object (see https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#swagger-object).
*
* @param {function} [callback]
* - It will be called when the API has been parsed, validated, and dereferenced, or when an error occurs.
*/
this.init = function(swagger, callback) {
//the swagger variable should only ever be a string or a populated object.
var invalidSwagger = _.isFunction(swagger) || _.isDate(swagger) || _.isEmpty(swagger);
if (invalidSwagger) {
throw new Error('Expected a Swagger file or object');
}
//Need to retrieve the Swagger API and metadata from the Swagger .yaml or .json file.
var parser = new SwaggerParser();
parser.dereference(swagger, function(err, api) {
if (err) {
util.warn(err);
}
context.error = err;
context.api = api;
context.parser = parser;
context.emit('change');
if (_.isFunction(callback)) {
callback(err, self, context.api, context.parser);
}
});
};
/**
* Serves the Swagger API file(s) in JSON and YAML formats,
* so they can be used with third-party front-end tools like Swagger UI and Swagger Editor.
*
* @param {express#Router} [router]
* - Express routing options (e.g. `caseSensitive`, `strict`).
* If an Express Application or Router is passed, then its routing settings will be used.
*
* @param {fileServer.defaultOptions} [options]
* - Options for how the files are served (see {@link fileServer.defaultOptions})
*
* @returns {function[]}
*/
this.files = function(router, options) {
if (arguments.length === 1 && !util.isExpressRouter(router) && !util.isExpressRoutingOptions(router)) {
// Shift arguments
options = router;
router = sharedRouter;
}
return fileServer(context, router, options);
};
/**
* Annotates the HTTP request (the `req` object) with Swagger metadata.
* This middleware populates {@link Request#swagger}.
*
* @param {express#Router} [router]
* - Express routing options (e.g. `caseSensitive`, `strict`).
* If an Express Application or Router is passed, then its routing settings will be used.
*
* @returns {function[]}
*/
this.metadata = function(router) {
return requestMetadata(context, router);
};
/**
* Handles CORS preflight requests and sets CORS headers for all requests
* according the Swagger API definition.
*
* @returns {function[]}
*/
this.CORS = function() {
return CORS();
};
/**
* Parses the HTTP request into typed values.
* This middleware populates {@link Request#params}, {@link Request#headers}, {@link Request#cookies},
* {@link Request#signedCookies}, {@link Request#query}, {@link Request#body}, and {@link Request#files}.
*
* @param {express#Router} [router]
* - An Express Application or Router. If provided, this will be used to register path-param middleware
* via {@link Router#param} (see http://expressjs.com/4x/api.html#router.param).
* If not provided, then path parameters will always be parsed as strings.
*
* @param {requestParser.defaultOptions} [options]
* - Options for each of the request-parsing middleware (see {@link requestParser.defaultOptions})
*
* @returns {function[]}
*/
this.parseRequest = function(router, options) {
if (arguments.length === 1 && !util.isExpressRouter(router) && !util.isExpressRoutingOptions(router)) {
// Shift arguments
options = router;
router = sharedRouter;
}
return req ...
n/a
function Resource(path, name, data) { switch (arguments.length) { case 0: this.collection = ''; this.name = '/'; this.data = undefined; break; case 1: this.collection = getCollectionFromPath(path); this.name = getNameFromPath(path); this.data = undefined; break; case 2: this.merge(name); this.collection = getCollectionFromPath(path); this.name = getNameFromPath(path); break; default: this.collection = normalizeCollection(path); this.name = normalizeName(name); this.merge(data); } this.createdOn = null; this.modifiedOn = null; }
n/a
function MiddlewareContext(router) {
events.EventEmitter.call(this);
/**
* Express routing options (e.g. `caseSensitive`, `strict`).
* If set to an Express Application or Router, then its routing settings will be used.
* @type {express#Router}
*/
this.router = router || {};
/**
* The parsed Swagger API
* @type {SwaggerObject}
*/
this.api = null;
/**
* The {@link SwaggerParser} instance that was used to parse the API.
* @type {SwaggerParser}
*/
this.parser = null;
/**
* If the Swagger API contains errors, this will be set
* @type {Error}
*/
this.error = null;
}
n/a
function JsonSchema(schema) { if (!schema) { throw ono({status: 500}, 'Missing JSON schema'); } if (schema.type !== undefined && dataTypes.indexOf(schema.type) === -1) { throw ono({status: 500}, 'Invalid JSON schema type: %s', schema.type); } this.schema = schema; }
n/a
function paramParser() { return [parseSimpleParams, parseFormDataParams, parseBodyParam]; }
n/a
function DataStore() {
/**
* The Express Application or Router that's used to determine case-sensitivity and/or strict matching
* of collection paths and resource names.
*
* @type {express#Router}
* @protected
*/
this.__router = {};
}
n/a
__openDataStore = function (collection, callback) {}
n/a
__saveDataStore = function (collection, resources, callback) {}
n/a
delete = function (resources, callback) { call(this, remove, arguments); }
...
);
// Add custom middleware
app.patch('/pets/:petName', function(req, res, next) {
if (req.body.name !== req.params.petName) {
// The pet's name has changed, so change its URL.
// Start by deleting the old resource
myDB.delete(new Resource(req.path), function(err, pet) {
if (pet) {
// Merge the new data with the old data
pet.merge(req.body);
}
else {
pet = req.body;
}
...
deleteCollection = function (collection, callback) { var self = this; openCollection(self, collection, function(err, collection, resources) { if (err) { doCallback(callback, err); } else { // Remove all resources in the collection var removed = _.remove(resources, collection.filter(self.__router, true)); if (removed.length > 0) { // Normalize the collection name var collectionName = collection.valueOf(self.__router, true); // Save the changes self.__saveDataStore(collectionName, resources, function(err) { if (err) { doCallback(callback, err); } else { doCallback(callback, null, removed); } }); } else { doCallback(callback, null, []); } } }); }
n/a
get = function (resource, callback) { var self = this; if (_.isString(resource)) { resource = new Resource(resource); } openCollection(self, resource, function(err, collection, resources) { if (err) { doCallback(callback, err); } else { // Find the resource resource = _.find(resources, resource.filter(self.__router)); doCallback(callback, null, resource); } }); }
...
res.set(header, responseHeaders[header]);
}
else {
// Set the header to a sensible default
switch (header) {
case accessControl.allowOrigin:
// By default, allow the origin host. Fallback to wild-card.
res.set(header, req.get('Origin') || '*');
break;
case accessControl.allowMethods:
if (req.swagger && req.swagger.path) {
// Return the allowed methods for this Swagger path
res.set(header, util.getAllowedMethods(req.swagger.path));
}
...
getCollection = function (collection, callback) { var self = this; openCollection(self, collection, function(err, collection, resources) { if (err) { doCallback(callback, err); } else { // Return the resources in the collection resources = _.filter(resources, collection.filter(self.__router, true)); doCallback(callback, null, resources); } }); }
n/a
remove = function (resources, callback) { call(this, remove, arguments); }
n/a
removeCollection = function (collection, callback) { var self = this; openCollection(self, collection, function(err, collection, resources) { if (err) { doCallback(callback, err); } else { // Remove all resources in the collection var removed = _.remove(resources, collection.filter(self.__router, true)); if (removed.length > 0) { // Normalize the collection name var collectionName = collection.valueOf(self.__router, true); // Save the changes self.__saveDataStore(collectionName, resources, function(err) { if (err) { doCallback(callback, err); } else { doCallback(callback, null, removed); } }); } else { doCallback(callback, null, []); } } }); }
n/a
save = function (resources, callback) { call(this, save, arguments); }
...
var app = express();
var middleware = new Middleware(app);
middleware.init(path.join(__dirname, 'PetStore.yaml'), function(err) {
// Create a custom data store with some initial mock data
var myDB = new MemoryDataStore();
myDB.save(
new Resource('/pets/Lassie', {name: 'Lassie', type: 'dog', tags: ['brown', 'white
']}),
new Resource('/pets/Clifford', {name: 'Clifford', type: 'dog', tags: ['red', 'big
']}),
new Resource('/pets/Garfield', {name: 'Garfield', type: 'cat', tags: ['orange']}),
new Resource('/pets/Snoopy', {name: 'Snoopy', type: 'dog', tags: ['black', 'white
']}),
new Resource('/pets/Hello%20Kitty', {name: 'Hello Kitty', type: 'cat', tags: ['white']})
);
...
function FileDataStore(baseDir) { DataStore.call(this); this.__baseDir = baseDir || process.cwd(); }
n/a
__openDataStore = function (collection, callback) { fs.readFile(getFilePath(this.__baseDir, collection), {encoding: 'utf8'}, function(err, data) { if (err) { if (err.code === 'ENOENT') { // The file doesn't exist yet, so just return an empty array callback(null, []); } else { callback(err); } } else { var resources; try { // Parse the JSON data into an array of Resource objects resources = Resource.parse(data); } catch (e) { callback(e); return; } // Call the callback outside of the try..catch block, // so we don't catch any errors that happen in third-party code callback(null, resources); } }); }
n/a
__saveDataStore = function (collection, resources, callback) { var self = this; // Create the directory path mkdirp(getDirectory(this.__baseDir, collection), function(err) { if (err) { callback(err); } else { // Write the JSON data to the file fs.writeFile(getFilePath(self.__baseDir, collection), JSON.stringify(resources, null, 2), callback); } }); }
n/a
function FileDataStore(baseDir) { DataStore.call(this); this.__baseDir = baseDir || process.cwd(); }
n/a
function MemoryDataStore() {
DataStore.call(this);
/**
* This implementation of DataStore uses an in-memory array.
* @type {Resource[]}
* @private
*/
this.__resourceStore = [];
}
n/a
__openDataStore = function (collection, callback) { setImmediate(callback, null, this.__resourceStore); }
n/a
__saveDataStore = function (collection, resources, callback) { try { this.__resourceStore = Resource.parse(resources); setImmediate(callback); } catch (e) { callback(e); } }
n/a
function MemoryDataStore() {
DataStore.call(this);
/**
* This implementation of DataStore uses an in-memory array.
* @type {Resource[]}
* @private
*/
this.__resourceStore = [];
}
n/a
function Resource(path, name, data) { switch (arguments.length) { case 0: this.collection = ''; this.name = '/'; this.data = undefined; break; case 1: this.collection = getCollectionFromPath(path); this.name = getNameFromPath(path); this.data = undefined; break; case 2: this.merge(name); this.collection = getCollectionFromPath(path); this.name = getNameFromPath(path); break; default: this.collection = normalizeCollection(path); this.name = normalizeName(name); this.merge(data); } this.createdOn = null; this.modifiedOn = null; }
n/a
parse = function (json) { if (!_.isString(json)) { // Convert the data to JSON, to match real-world serialization json = JSON.stringify(json); } json = JSON.parse(json); var isArray = _.isArray(json); if (!isArray) { json = [json]; } var resources = []; json.forEach(function(pojo) { var resource = new Resource(pojo.collection, pojo.name, pojo.data); resource.createdOn = new Date(pojo.createdOn); resource.modifiedOn = new Date(pojo.modifiedOn); resources.push(resource); }); return isArray ? resources : resources[0]; }
...
else if (schema.default === undefined) {
// The parameter is optional, and there's no default value
return undefined;
}
}
try {
return new JsonSchema(schema).parse(value);
}
catch (e) {
throw ono(e, {status: e.status}, 'The "%s" %s parameter is invalid (%j)',
param.name, param.in, value === undefined ? param.default : value);
}
}
...
filter = function (router, collectionOnly) { var myValue = this.valueOf(router, collectionOnly); return function(resource) { return myValue === resource.valueOf(router, collectionOnly); }; }
...
* This is useful for setting HTTP headers such as Allow and Access-Control-Allow-Methods.
*
* @param {object} path - A Path object, from the Swagger API.
* @returns {string}
*/
exports.getAllowedMethods = function(path) {
return swaggerMethods
.filter(function(method) { return !!path[method]; })
.join(', ')
.toUpperCase();
};
/**
* Returns the given operation's Response objects that have HTTP response codes between
* the given min and max (inclusive).
...
merge = function (other) { this.modifiedOn = new Date(); var otherData = other ? other instanceof Resource ? other.data : other : other; // Merge with the other resource's data, if possible; otherwise, overwrite. if ((_.isArray(this.data) && _.isArray(otherData)) || (_.isPlainObject(this.data) && _.isPlainObject(otherData))) { _.merge(this.data, otherData); } else { this.data = otherData; } }
...
* @param {fileServer.defaultOptions} [options]
* @returns {function[]}
*/
function fileServer(context, router, options) {
router = router || context.router;
// Override default options
options = _.merge({}, fileServer.defaultOptions, options);
// Only return the middleware that's allowed
var middleware = [];
options.apiPath && middleware.push(serveDereferencedSwaggerFile);
options.rawFilesPath && middleware.push(serveRawSwaggerFiles);
return middleware;
...
toString = function () { return this.collection + this.name; }
...
function parseBoolean(schema, value, propPath) {
// Handle missing, required, and default
value = getValueToValidate(schema, value);
// "Parse" the value
var parsedValue = value;
var stringValue = _(value).toString().toLowerCase();
if (stringValue === 'true') {
parsedValue = true;
}
else if (stringValue === 'false') {
parsedValue = false;
}
...
valueOf = function (router, collectionOnly) { if (router) { var myValue = collectionOnly ? this.collection : this.toString(); return util.normalizePath(myValue, router); } else { return this.toString(); } }
...
return _(value).toString();
}
}
function sampleDate(schema) {
var min, max;
if (schema.minimum !== undefined) {
min = parseInt(new Date(schema.minimum).valueOf()) + (schema.exclusiveMinimum ? 1 :
0);
}
else {
min = Date.UTC(1970, 0, 1);
min = Math.min(min, new Date(schema.maximum).valueOf()) || min;
}
if (schema.maximum !== undefined) {
...
function MiddlewareContext(router) {
events.EventEmitter.call(this);
/**
* Express routing options (e.g. `caseSensitive`, `strict`).
* If set to an Express Application or Router, then its routing settings will be used.
* @type {express#Router}
*/
this.router = router || {};
/**
* The parsed Swagger API
* @type {SwaggerObject}
*/
this.api = null;
/**
* The {@link SwaggerParser} instance that was used to parse the API.
* @type {SwaggerParser}
*/
this.parser = null;
/**
* If the Swagger API contains errors, this will be set
* @type {Error}
*/
this.error = null;
}
n/a
function addListener(type, listener) { return _addListener(this, type, listener, false); }
n/a
function emit(type) { var er, handler, len, args, i, events, domain; var needDomainExit = false; var doError = (type === 'error'); events = this._events; if (events) doError = (doError && events.error == null); else if (!doError) return false; domain = this.domain; // If there is no 'error' event listener then throw. if (doError) { er = arguments[1]; if (domain) { if (!er) er = new Error('Uncaught, unspecified "error" event'); er.domainEmitter = this; er.domain = domain; er.domainThrown = false; domain.emit('error', er); } else if (er instanceof Error) { throw er; // Unhandled 'error' event } else { // At least give some kind of context to the user var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); err.context = er; throw err; } return false; } handler = events[type]; if (!handler) return false; if (domain && this !== process) { domain.enter(); needDomainExit = true; } var isFn = typeof handler === 'function'; len = arguments.length; switch (len) { // fast cases case 1: emitNone(handler, isFn, this); break; case 2: emitOne(handler, isFn, this, arguments[1]); break; case 3: emitTwo(handler, isFn, this, arguments[1], arguments[2]); break; case 4: emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); break; // slower default: args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; emitMany(handler, isFn, this, args); } if (needDomainExit) domain.exit(); return true; }
...
if (err) {
util.warn(err);
}
context.error = err;
context.api = api;
context.parser = parser;
context.emit('change');
if (_.isFunction(callback)) {
callback(err, self, context.api, context.parser);
}
});
};
...
function eventNames() { return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; }
n/a
function getMaxListeners() { return $getMaxListeners(this); }
n/a
function listenerCount(type) { const events = this._events; if (events) { const evlistener = events[type]; if (typeof evlistener === 'function') { return 1; } else if (evlistener) { return evlistener.length; } } return 0; }
n/a
function listeners(type) { var evlistener; var ret; var events = this._events; if (!events) ret = []; else { evlistener = events[type]; if (!evlistener) ret = []; else if (typeof evlistener === 'function') ret = [evlistener]; else ret = arrayClone(evlistener, evlistener.length); } return ret; }
n/a
function addListener(type, listener) { return _addListener(this, type, listener, false); }
...
router = util.isExpressRouter(router) ? router : context.router;
if (util.isExpressRouter(router)) {
// This is special path-param middleware, which sets `req.params`
registerPathParamMiddleware();
// If the API changes, register any new path-params
context.on('change', registerPathParamMiddleware);
}
else {
util.debug(
'WARNING! An Express Router/Application was not passed to the requestParser middleware. ' +
'req.params will not be parsed. Use req.pathParams instead.'
);
}
...
function once(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.on(type, _onceWrap(this, type, listener)); return this; }
n/a
function prependListener(type, listener) { return _addListener(this, type, listener, true); }
n/a
function prependOnceListener(type, listener) { if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); this.prependListener(type, _onceWrap(this, type, listener)); return this; }
n/a
function removeAllListeners(type) { var listeners, events; events = this._events; if (!events) return this; // not listening for removeListener, no need to emit if (!events.removeListener) { if (arguments.length === 0) { this._events = new EventHandlers(); this._eventsCount = 0; } else if (events[type]) { if (--this._eventsCount === 0) this._events = new EventHandlers(); else delete events[type]; } return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { var keys = Object.keys(events); for (var i = 0, key; i < keys.length; ++i) { key = keys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = new EventHandlers(); this._eventsCount = 0; return this; } listeners = events[type]; if (typeof listeners === 'function') { this.removeListener(type, listeners); } else if (listeners) { // LIFO order do { this.removeListener(type, listeners[listeners.length - 1]); } while (listeners[0]); } return this; }
n/a
function removeListener(type, listener) { var list, events, position, i, originalListener; if (typeof listener !== 'function') throw new TypeError('"listener" argument must be a function'); events = this._events; if (!events) return this; list = events[type]; if (!list) return this; if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) this._events = new EventHandlers(); else { delete events[type]; if (events.removeListener) this.emit('removeListener', type, list.listener || listener); } } else if (typeof list !== 'function') { position = -1; for (i = list.length; i-- > 0;) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; break; } } if (position < 0) return this; if (list.length === 1) { list[0] = undefined; if (--this._eventsCount === 0) { this._events = new EventHandlers(); return this; } else { delete events[type]; } } else { spliceOne(list, position); } if (events.removeListener) this.emit('removeListener', type, originalListener || listener); } return this; }
n/a
function setMaxListeners(n) { if (typeof n !== 'number' || n < 0 || isNaN(n)) throw new TypeError('"n" argument must be a positive number'); this._maxListeners = n; return this; }
n/a
function JsonSchema(schema) { if (!schema) { throw ono({status: 500}, 'Missing JSON schema'); } if (schema.type !== undefined && dataTypes.indexOf(schema.type) === -1) { throw ono({status: 500}, 'Invalid JSON schema type: %s', schema.type); } this.schema = schema; }
n/a
parse = function (value, propPath) { switch (this.schema.type) { case 'number': return parseNumber(this.schema, value, propPath); case 'integer': return parseInteger(this.schema, value, propPath); case 'boolean': return parseBoolean(this.schema, value, propPath); case 'array': return parseArray(this.schema, value, propPath); case 'object': case undefined: return parseObject(this.schema, value, propPath); case 'file': return parseFile(this.schema, value, propPath); case 'string': switch (this.schema.format) { case 'byte': return parseInteger(this.schema, value, propPath); case 'date': case 'date-time': return parseDate(this.schema, value, propPath); default: return parseString(this.schema, value, propPath); } } }
...
else if (schema.default === undefined) {
// The parameter is optional, and there's no default value
return undefined;
}
}
try {
return new JsonSchema(schema).parse(value);
}
catch (e) {
throw ono(e, {status: e.status}, 'The "%s" %s parameter is invalid (%j)',
param.name, param.in, value === undefined ? param.default : value);
}
}
...
sample = function () { switch (this.schema.type) { case 'number': return sampleNumber(this.schema); case 'integer': return sampleInteger(this.schema); case 'boolean': return sampleBoolean(this.schema); case 'array': return sampleArray(this.schema); case 'object': case undefined: return sampleObject(this.schema); case 'string': switch (this.schema.format) { case 'byte': return sampleInteger(this.schema); case 'date': case 'date-time': return sampleDate(this.schema); default: return sampleString(this.schema); } } }
...
maxItems = Math.max(50, minItems);
}
var array = [];
var itemSchema = new JsonSchema(schema.items);
var length = _.random(minItems, maxItems);
for (var i = 0; i < length; i++) {
array.push(itemSchema.sample());
}
return array;
}
function parseObject(schema, value, propPath) {
// Handle missing, required, and default
...
serialize = function (value, propPath) { switch (this.schema.type) { case 'number': return serializeNumber(this.schema, value, propPath); case 'integer': return serializeInteger(this.schema, value, propPath); case 'boolean': return serializeBoolean(this.schema, value, propPath); case 'array': return serializeArray(this.schema, value, propPath); case 'object': case undefined: return serializeObject(this.schema, value, propPath); case 'file': return serializeFile(this.schema, value, propPath); case 'string': switch (this.schema.format) { case 'byte': return serializeInteger(this.schema, value, propPath); case 'date': case 'date-time': return serializeDate(this.schema, value, propPath); default: return serializeString(this.schema, value, propPath); } } }
...
function serializeArray(schema, value, propPath) {
value = getValueToValidate(schema, value);
if (_.isArray(value) && schema.items) {
var itemSchema = new JsonSchema(schema.items);
for (var i = 0; i < value.length; i++) {
value[i] = itemSchema.serialize(value[i], propPath + '[' + i + ']
x27;);
}
}
return value;
}
function sampleArray(schema) {
...
function paramParser() { return [parseSimpleParams, parseFormDataParams, parseBodyParam]; }
n/a
function parseParameter(param, value, schema) { if (value === undefined) { if (param.required) { // The parameter is required, but was not provided, so throw a 400 error var errCode = 400; if (param.in === 'header' && param.name.toLowerCase() === 'content-length') { // Special case for the Content-Length header. It has it's own HTTP error code. errCode = 411; // (Length Required) } //noinspection ExceptionCaughtLocallyJS throw ono({status: errCode}, 'Missing required %s parameter "%s"', param.in, param.name); } else if (schema.default === undefined) { // The parameter is optional, and there's no default value return undefined; } } try { return new JsonSchema(schema).parse(value); } catch (e) { throw ono(e, {status: e.status}, 'The "%s" %s parameter is invalid (%j)', param.name, param.in, value === undefined ? param.default : value); } }
...
// Parse each path param
for (var i = 1; i < values.length; i++) {
var paramName = paramNames[i - 1];
var paramValue = decodeURIComponent(values[i]);
var param = _.find(req.swagger.params, {in: 'path', name: paramName});
util.debug(' Parsing the "%s" path parameter', paramName);
req.pathParams[paramName] = paramParser.parseParameter(param, paramValue, param
);
}
}
}
next();
}
}
...
function debug() { // disabled? if (!debug.enabled) return; var self = debug; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // turn the `arguments` into a proper Array var args = new Array(arguments.length); for (var i = 0; i < args.length; i++) { args[i] = arguments[i]; } args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %O args.unshift('%O'); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); // apply env-specific formatting (colors, etc.) exports.formatArgs.call(self, args); var logFn = debug.log || exports.log || console.log.bind(console); logFn.apply(self, args); }
...
}
/**
* Handles CORS preflight requests.
*/
function corsPreflight(req, res, next) {
if (req.method === 'OPTIONS') {
util.debug('OPTIONS %s is a CORS preflight request. Sending HTTP 200 response
.', req.path);
res.send();
}
else {
next();
}
}
...
getAllowedMethods = function (path) { return swaggerMethods .filter(function(method) { return !!path[method]; }) .join(', ') .toUpperCase(); }
...
// By default, allow the origin host. Fallback to wild-card.
res.set(header, req.get('Origin') || '*');
break;
case accessControl.allowMethods:
if (req.swagger && req.swagger.path) {
// Return the allowed methods for this Swagger path
res.set(header, util.getAllowedMethods(req.swagger.path));
}
else {
// By default, allow all of the requested methods. Fallback to ALL methods.
res.set(header, req.get('Access-Control-Request-Method') || swaggerMethods.join(', ').toUpperCase());
}
break;
...
getParameters = function (path, operation) { var pathParams = [], operationParams = []; // Get the path and operation parameters if (path && path.parameters) { pathParams = path.parameters; } if (operation && operation.parameters) { operationParams = operation.parameters; } // Combine the path and operation parameters, // with the operation params taking precedence over the path params return _.unique(operationParams.concat(pathParams), function(param) { return param.name + param.in; }); }
...
next();
}
/**
* Sets `req.swagger.params`
*/
function swaggerParamsMetadata(req, res, next) {
req.swagger.params = util.getParameters(req.swagger.path, req.swagger.operation);
next();
}
/**
* Sets `req.swagger.security`
*/
function swaggerSecurityMetadata(req, res, next) {
...
getRequestSchema = function (path, operation) { var params = exports.getParameters(path, operation); // If there's a "body" parameter, then use its schema var bodyParam = _.find(params, {in: 'body'}); if (bodyParam) { if (bodyParam.schema.type === 'array') { return bodyParam.schema.items; } else { return bodyParam.schema; } } else { var schema = {type: 'object', required: [], properties: {}}; // If there are "formData" parameters, then concatenate them into a single JSON schema _.where(params, {in: 'formData'}).forEach(function(param) { schema.properties[param.name] = param; if (param.required) { schema.required.push(param.name); } }); return schema; } }
n/a
getResponsesBetween = function (operation, min, max) { return _.map(operation.responses, function(response, responseCode) { return { code: parseInt(responseCode) || responseCode, api: response }; }) .sort(function(a, b) { // Sort by response code. "default" comes last. a = _.isNumber(a.code) ? a.code : 999; b = _.isNumber(b.code) ? b.code : 999; return a - b; }) .filter(function(response) { return (response.code >= min && response.code <= max) || _.isString(response.code); }); }
n/a
isExpressApp = function (router) { return exports.isExpressRouter(router) && _.isFunction(router.get) && _.isFunction(router.set) && _.isFunction(router.enabled) && _.isFunction(router.disabled); }
...
exports.normalizePath = function(path, router) {
var caseSensitive, strict;
if (!path) {
return '';
}
if (exports.isExpressApp(router)) {
caseSensitive = router.enabled('case sensitive routing');
strict = router.enabled('strict routing');
}
else {
// This could be an Express Router, or a POJO
caseSensitive = !!router.caseSensitive;
strict = !!router.strict;
...
isExpressRouter = function (router) { return _.isFunction(router) && _.isFunction(router.param); }
...
* - An Express Application or Router. If provided, this will be used to determine routing settings
* (case sensitivity, strictness), and to register path-param middleware via {@link Router#param}
* (see http://expressjs.com/4x/api.html#router.param).
*
* @constructor
*/
function Middleware(sharedRouter) {
sharedRouter = util.isExpressRouter(sharedRouter) ? sharedRouter : undefined;
var self = this;
var context = new MiddlewareContext(sharedRouter);
/**
* Initializes the middleware with the given Swagger API.
* This method can be called again to re-initialize with a new or modified API.
...
isExpressRoutingOptions = function (router) { return _.isObject(router) && ('caseSensitive' in router || 'strict' in router || 'mergeParams' in router); }
...
*
* @param {fileServer.defaultOptions} [options]
* - Options for how the files are served (see {@link fileServer.defaultOptions})
*
* @returns {function[]}
*/
this.files = function(router, options) {
if (arguments.length === 1 && !util.isExpressRouter(router) && !util.isExpressRoutingOptions
(router)) {
// Shift arguments
options = router;
router = sharedRouter;
}
return fileServer(context, router, options);
};
...
isSwaggerRequest = function (req) { // If req.swagger.operation is set, then so are req.swagger.api and req.swagger.path return req.swagger && req.swagger.operation; }
...
}
/**
* Parses all Swagger path parameters and sets `req.pathParams`.
* NOTE: This middleware cannot set `req.params`. That requires special path-param middleware (see above)
*/
function parsePathParams(req, res, next) {
if (util.isSwaggerRequest(req)) {
req.pathParams = {};
if (req.swagger.pathName.indexOf('{') >= 0) {
// Convert the Swagger path to a RegExp
var paramNames = [];
var pathPattern = req.swagger.pathName.replace(util.swaggerParamRegExp, function(match, paramName) {
paramNames.push(paramName);
...
normalizePath = function (path, router) { var caseSensitive, strict; if (!path) { return ''; } if (exports.isExpressApp(router)) { caseSensitive = router.enabled('case sensitive routing'); strict = router.enabled('strict routing'); } else { // This could be an Express Router, or a POJO caseSensitive = !!router.caseSensitive; strict = !!router.strict; } if (!caseSensitive) { path = path.toLowerCase(); } if (!strict && _.endsWith(path, '/')) { path = path.substr(0, path.length - 1); } return path; }
...
/**
* Serves the fully-dereferenced Swagger API in JSON format.
*/
function serveDereferencedSwaggerFile(req, res, next) {
if (req.method === 'GET' || req.method === 'HEAD') {
var configPath = getConfiguredPath(options.apiPath);
configPath = util.normalizePath(configPath, router);
var reqPath = util.normalizePath(req.path, router);
if (reqPath === configPath) {
if (context.api) {
util.debug('%s %s => Sending the Swagger API as JSON', req.method, req.path);
res.json(context.api);
}
...
rfc1123 = function (date) { // jscs:disable maximumLineLength var dayName = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][date.getUTCDay()]; var monthName = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][date.getUTCMonth()]; return [ dayName, ', ', _.padLeft(date.getUTCDate(), 2, '0'), ' ', monthName, ' ', date.getUTCFullYear(), ' ', _.padLeft(date.getUTCHours(), 2, '0'), ':', _.padLeft(date.getUTCMinutes(), 2, '0'), ':', _.padLeft(date.getUTCSeconds(), 2, ' 0'), ' GMT' ].join(''); // jscs:enable maximumLineLength }
n/a
warn = function (err, message, params) { if (process.env.WARN !== 'off') { if (_.isString(err)) { console.warn(format.apply(null, arguments)); } else if (arguments.length > 1) { console.warn(format.apply(null, _.drop(arguments, 1)) + ' \n' + err.stack); } else { console.warn(err.stack); } } }
...
if (reqPath === configPath) {
if (context.api) {
util.debug('%s %s => Sending the Swagger API as JSON', req.method, req.path);
res.json(context.api);
}
else {
util.warn('WARNING! The Swagger API is empty. Sending an HTTP 500 response
to %s %s', req.method, req.path);
res.status(500).json({});
}
return;
}
}
next();
...