function initializeMiddleware(rlOrSO, resources, callback) { var args; var spec; debug('Initializing middleware'); if (_.isUndefined(rlOrSO)) { throw new Error('rlOrSO is required'); } else if (!_.isPlainObject(rlOrSO)) { throw new TypeError('rlOrSO must be an object'); } args = [rlOrSO]; spec = helpers.getSpec(helpers.getSwaggerVersion(rlOrSO), true); debug(' Identified Swagger version: %s', spec.version); if (spec.version === '1.2') { if (_.isUndefined(resources)) { throw new Error('resources is required'); } else if (!_.isArray(resources)) { throw new TypeError('resources must be an array'); } debug(' Number of API Declarations: %d', resources.length); args.push(resources); } else { callback = arguments[1]; } if (_.isUndefined(callback)) { throw new Error('callback is required'); } else if (!_.isFunction(callback)) { throw new TypeError('callback must be a function'); } args.push(function (err, results) { if (results && results.errors.length + _.reduce(results.apiDeclarations || [], function (count, apiDeclaration) { return count += (apiDeclaration ? apiDeclaration.errors.length : 0); }, 0) > 0) { err = new Error('Swagger document(s) failed validation so the server cannot start'); err.failedValidation = true; err.results = results; } debug(' Validation: %s', err ? 'failed' : 'succeeded'); try { if (err) { throw err; } callback({ // Create a wrapper to avoid having to pass the non-optional arguments back to the swaggerMetadata middleware swaggerMetadata: function () { var swaggerMetadata = require('./middleware/swagger-metadata'); return swaggerMetadata.apply(undefined, args.slice(0, args.length - 1)); }, swaggerRouter: require('./middleware/swagger-router'), swaggerSecurity: require('./middleware/swagger-security'), // Create a wrapper to avoid having to pass the non-optional arguments back to the swaggerUi middleware swaggerUi: function (options) { var swaggerUi = require('./middleware/swagger-ui'); var suArgs = [rlOrSO]; if (spec.version === '1.2') { suArgs.push(_.reduce(resources, function (map, resource) { map[resource.resourcePath] = resource; return map; }, {})); } suArgs.push(options || {}); return swaggerUi.apply(undefined, suArgs); }, swaggerValidator: require('./middleware/swagger-validator') }); } catch (err) { if (process.env.RUNNING_SWAGGER_TOOLS_TESTS === 'true') { // When running the swagger-tools test suite, we want to return an error instead of exiting the process. This // does not mean that this function is an error-first callback but due to json-refs using Promises, we have to // return the error to avoid the error being swallowed. callback(err); } else { if (err.failedValidation === true) { helpers.printValidationResults(spec.version, rlOrSO, resources, results, true); } else { console.error('Error initializing middleware'); console.error(err.stack); } process.exit(1); } } }); spec.validate.apply(spec, args); }
n/a
createJsonValidator = function (schemas) { var validator = new ZSchema({ breakOnFirstError: false, reportPathAsArray: true }); var result; // Add the draft-04 spec validator.setRemoteReference(draft04Url, draft04Json); // Swagger uses some unsupported/invalid formats so just make them all pass _.each(customJsonSchemaFormats, function (format) { ZSchema.registerFormat(format, function () { return true; }); }); // Compile and validate the schemas if (!_.isUndefined(schemas)) { result = validator.compileSchema(schemas); // If there is an error, it's unrecoverable so just blow the eff up if (result === false) { console.error('JSON Schema file' + (schemas.length > 1 ? 's are' : ' is') + ' invalid:'); _.each(validator.getLastErrors(), function (err) { console.error(' ' + (_.isArray(err.path) ? JsonRefs.pathToPtr(err.path) : err.path) + ': ' + err.message); }); throw new Error('Unable to create validator due to invalid JSON Schema'); } } return validator; }
...
.catch(callback);
} else {
callback();
}
};
var validateAgainstSchema = function (spec, schemaOrName, data, callback) {
var validator = _.isString(schemaOrName) ? spec.validators[schemaOrName] : helpers.createJsonValidator
();
helpers.registerCustomFormats(data);
try {
validators.validateAgainstSchema(schemaOrName, data, validator);
} catch (err) {
if (err.failedValidation) {
...
formatResults = function (results) { if (results) { // Update the results based on its content to indicate success/failure accordingly results = (results.errors.length + results.warnings.length + _.reduce(results.apiDeclarations, function (count, aResult) { if (aResult) { count += aResult.errors.length + aResult.warnings.length; } return count; }, 0) > 0) ? results : undefined; } return results; }
...
validateDefinitions(documentMetadata, results);
callback(undefined, results);
};
var validateSemantically = function (spec, rlOrSO, apiDeclarations, callback) {
var cbWrapper = function (err, results) {
callback(err, helpers.formatResults(results));
};
if (spec.version === '1.2') {
validateSwagger1_2(spec, rlOrSO, apiDeclarations, cbWrapper); // jshint ignore:line
} else {
validateSwagger2_0(spec, rlOrSO, cbWrapper); // jshint ignore:line
}
};
...
getErrorCount = function (results) { var errors = 0; if (results) { errors = results.errors.length; _.each(results.apiDeclarations, function (adResults) { if (adResults) { errors += adResults.errors.length; } }); } return errors; }
...
Specification.prototype.composeModel = function (apiDOrSO, modelIdOrRef, callback) {
var swaggerVersion = helpers.getSwaggerVersion(apiDOrSO);
var doComposition = function (err, results) {
var documentMetadata;
if (err) {
return callback(err);
} else if (helpers.getErrorCount(results) > 0) {
return handleValidationError(results, callback);
}
documentMetadata = getDocumentCache(apiDOrSO);
results = {
errors: [],
warnings: []
...
getSpec = function (version, throwError) { var spec; version = coerceVersion(version); spec = specCache[version]; if (_.isUndefined(spec)) { switch (version) { case '1.2': spec = require('../lib/specs').v1_2; // jshint ignore:line break; case '2.0': spec = require('../lib/specs').v2_0; // jshint ignore:line break; default: if (throwError === true) { throw new Error('Unsupported Swagger version: ' + version); } } } return spec; }
...
if (_.isUndefined(rlOrSO)) {
throw new Error('rlOrSO is required');
} else if (!_.isPlainObject(rlOrSO)) {
throw new TypeError('rlOrSO must be an object');
}
args = [rlOrSO];
spec = helpers.getSpec(helpers.getSwaggerVersion(rlOrSO), true);
debug(' Identified Swagger version: %s', spec.version);
if (spec.version === '1.2') {
if (_.isUndefined(resources)) {
throw new Error('resources is required');
} else if (!_.isArray(resources)) {
...
getSwaggerVersion = function (document) { return _.isPlainObject(document) ? coerceVersion(document.swaggerVersion || document.swagger) : undefined; }
...
if (_.isUndefined(rlOrSO)) {
throw new Error('rlOrSO is required');
} else if (!_.isPlainObject(rlOrSO)) {
throw new TypeError('rlOrSO must be an object');
}
args = [rlOrSO];
spec = helpers.getSpec(helpers.getSwaggerVersion(rlOrSO), true);
debug(' Identified Swagger version: %s', spec.version);
if (spec.version === '1.2') {
if (_.isUndefined(resources)) {
throw new Error('resources is required');
} else if (!_.isArray(resources)) {
...
printValidationResults = function (version, apiDOrSO, apiDeclarations, results, printSummary) { var hasErrors = getErrorCount(results) > 0; var stream = hasErrors ? console.error : console.log; var pluralize = function (string, count) { return count === 1 ? string : string + 's'; }; var printErrorsOrWarnings = function (header, entries, indent) { if (header) { stream(header + ':'); stream(); } _.each(entries, function (entry) { stream(new Array(indent + 1).join(' ') + JsonRefs.pathToPtr(entry.path) + ': ' + entry.message); if (entry.inner) { printErrorsOrWarnings (undefined, entry.inner, indent + 2); } }); if (header) { stream(); } }; var errorCount = 0; var warningCount = 0; stream(); if (results.errors.length > 0) { errorCount += results.errors.length; printErrorsOrWarnings('API Errors', results.errors, 2); } if (results.warnings.length > 0) { warningCount += results.warnings.length; printErrorsOrWarnings('API Warnings', results.warnings, 2); } if (results.apiDeclarations) { results.apiDeclarations.forEach(function (adResult, index) { if (!adResult) { return; } var name = apiDeclarations[index].resourcePath || index; if (adResult.errors.length > 0) { errorCount += adResult.errors.length; printErrorsOrWarnings(' API Declaration (' + name + ') Errors', adResult.errors, 4); } if (adResult.warnings.length > 0) { warningCount += adResult.warnings.length; printErrorsOrWarnings(' API Declaration (' + name + ') Warnings', adResult.warnings, 4); } }); } if (printSummary) { if (errorCount > 0) { stream(errorCount + ' ' + pluralize('error', errorCount) + ' and ' + warningCount + ' ' + pluralize('warning', warningCount)); } else { stream('Validation succeeded but with ' + warningCount + ' ' + pluralize('warning', warningCount)); } } stream(); }
...
if (process.env.RUNNING_SWAGGER_TOOLS_TESTS === 'true') {
// When running the swagger-tools test suite, we want to return an error instead of exiting the process. This
// does not mean that this function is an error-first callback but due to json-refs using Promises, we have to
// return the error to avoid the error being swallowed.
callback(err);
} else {
if (err.failedValidation === true) {
helpers.printValidationResults(spec.version, rlOrSO, resources, results, true);
} else {
console.error('Error initializing middleware');
console.error(err.stack);
}
process.exit(1);
}
...
registerCustomFormats = function (json) { traverse(json).forEach(function () { var name = this.key; var format = this.node; if (name === 'format' && _.indexOf(ZSchema.getRegisteredFormats(), format) === -1) { ZSchema.registerFormat(format, function () { return true; }); } }); }
...
callback();
}
};
var validateAgainstSchema = function (spec, schemaOrName, data, callback) {
var validator = _.isString(schemaOrName) ? spec.validators[schemaOrName] : helpers.createJsonValidator();
helpers.registerCustomFormats(data);
try {
validators.validateAgainstSchema(schemaOrName, data, validator);
} catch (err) {
if (err.failedValidation) {
return callback(undefined, err.results);
} else {
...
validateAgainstSchema = function (schemaOrName, data, validator) { var sanitizeError = function (obj) { // Make anyOf/oneOf errors more human readable (Issue 200) var defType = ['additionalProperties', 'items'].indexOf(obj.path[obj.path.length - 1]) > -1 ? 'schema' : obj.path[obj.path.length - 2]; if (['ANY_OF_MISSING', 'ONE_OF_MISSING'].indexOf(obj.code) > -1) { switch (defType) { case 'parameters': defType = 'parameter'; break; case 'responses': defType = 'response'; break; case 'schema': defType += ' ' + obj.path[obj.path.length - 1]; // no default } obj.message = 'Not a valid ' + defType + ' definition'; } // Remove the params portion of the error delete obj.params; delete obj.schemaId; if (obj.inner) { _.each(obj.inner, function (nObj) { sanitizeError(nObj); }); } }; var schema = _.isPlainObject(schemaOrName) ? _.cloneDeep(schemaOrName) : schemaOrName; // We don't check this due to internal usage but if validator is not provided, schemaOrName must be a schema if (_.isUndefined(validator)) { validator = helpers.createJsonValidator([schema]); } var valid = validator.validate(data, schema); if (!valid) { try { throwErrorWithCode('SCHEMA_VALIDATION_FAILED', 'Failed schema validation'); } catch (err) { err.results = { errors: _.map(validator.getLastErrors(), function (err) { sanitizeError(err); return err; }), warnings: [] }; throw err; } } }
...
var validateAgainstSchema = function (spec, schemaOrName, data, callback) {
var validator = _.isString(schemaOrName) ? spec.validators[schemaOrName] : helpers.createJsonValidator();
helpers.registerCustomFormats(data);
try {
validators.validateAgainstSchema(schemaOrName, data, validator);
} catch (err) {
if (err.failedValidation) {
return callback(undefined, err.results);
} else {
return callback(err);
}
}
...
validateArrayType = function (schema) { // We have to do this manually for now if (schema.type === 'array' && _.isUndefined(schema.items)) { throwErrorWithCode('OBJECT_MISSING_REQUIRED_PROPERTY', 'Missing required property: items'); } }
n/a
validateContentType = function (gPOrC, oPOrC, reqOrRes) { // http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1 var isResponse = typeof reqOrRes.end === 'function'; var contentType = isResponse ? reqOrRes.getHeader('content-type') : reqOrRes.headers['content-type']; var pOrC = _.map(_.union(gPOrC, oPOrC), function (contentType) { return contentType.split(';')[0]; }); if (!contentType) { if (isResponse) { contentType = 'text/plain'; } else { contentType = 'application/octet-stream'; } } contentType = contentType.split(';')[0]; if (pOrC.length > 0 && (isResponse ? true : ['POST', 'PUT'].indexOf(reqOrRes.method) !== -1) && pOrC.indexOf(contentType) === -1) { throw new Error('Invalid content type (' + contentType + '). These are valid: ' + pOrC.join(', ')); } }
...
sendData(swaggerVersion, res, data, encoding, true);
return; // do NOT call next() here, doing so would execute remaining middleware chain twice
}
try {
// Validate the content type
try {
validators.validateContentType(req.swagger.apiDeclaration ?
req.swagger.apiDeclaration.produces :
req.swagger.swaggerObject.produces,
operation.produces, res);
} catch (err) {
err.failedValidation = true;
throw err;
...
validateEnum = function (val, allowed) { if (!_.isUndefined(allowed) && !_.isUndefined(val) && allowed.indexOf(val) === -1) { throwErrorWithCode('ENUM_MISMATCH', 'Not an allowable value (' + allowed.join(', ') + '): ' + val); } }
n/a
validateMaxItems = function (val, maxItems) { if (!_.isUndefined(maxItems) && val.length > maxItems) { throwErrorWithCode('ARRAY_LENGTH_LONG', 'Array is too long (' + val.length + '), maximum ' + maxItems); } }
n/a
validateMaxLength = function (val, maxLength) { if (!_.isUndefined(maxLength) && val.length > maxLength) { throwErrorWithCode('MAX_LENGTH', 'String is too long (' + val.length + ' chars), maximum ' + maxLength); } }
n/a
validateMaxProperties = function (val, maxProperties) { var propCount = _.isPlainObject(val) ? Object.keys(val).length : 0; if (!_.isUndefined(maxProperties) && propCount > maxProperties) { throwErrorWithCode('MAX_PROPERTIES', 'Number of properties is too many (' + propCount + ' properties), maximum ' + maxProperties); } }
n/a
validateMaximum = function (val, maximum, type, exclusive) { var code = exclusive === true ? 'MAXIMUM_EXCLUSIVE' : 'MAXIMUM'; var testMax; var testVal; if (_.isUndefined(exclusive)) { exclusive = false; } if (type === 'integer') { testVal = parseInt(val, 10); } else if (type === 'number') { testVal = parseFloat(val); } if (!_.isUndefined(maximum)) { testMax = parseFloat(maximum); if (exclusive && testVal >= testMax) { throwErrorWithCode(code, 'Greater than or equal to the configured maximum (' + maximum + '): ' + val); } else if (testVal > testMax) { throwErrorWithCode(code, 'Greater than the configured maximum (' + maximum + '): ' + val); } } }
n/a
validateMinItems = function (val, minItems) { if (!_.isUndefined(minItems) && val.length < minItems) { throwErrorWithCode('ARRAY_LENGTH_SHORT', 'Array is too short (' + val.length + '), minimum ' + minItems); } }
n/a
validateMinLength = function (val, minLength) { if (!_.isUndefined(minLength) && val.length < minLength) { throwErrorWithCode('MIN_LENGTH', 'String is too short (' + val.length + ' chars), minimum ' + minLength); } }
n/a
validateMinProperties = function (val, minProperties) { var propCount = _.isPlainObject(val) ? Object.keys(val).length : 0; if (!_.isUndefined(minProperties) && propCount < minProperties) { throwErrorWithCode('MIN_PROPERTIES', 'Number of properties is too few (' + propCount + ' properties), minimum ' + minProperties); } }
n/a
validateMinimum = function (val, minimum, type, exclusive) { var code = exclusive === true ? 'MINIMUM_EXCLUSIVE' : 'MINIMUM'; var testMin; var testVal; if (_.isUndefined(exclusive)) { exclusive = false; } if (type === 'integer') { testVal = parseInt(val, 10); } else if (type === 'number') { testVal = parseFloat(val); } if (!_.isUndefined(minimum)) { testMin = parseFloat(minimum); if (exclusive && testVal <= testMin) { throwErrorWithCode(code, 'Less than or equal to the configured minimum (' + minimum + '): ' + val); } else if (testVal < testMin) { throwErrorWithCode(code, 'Less than the configured minimum (' + minimum + '): ' + val); } } }
n/a
validateMultipleOf = function (val, multipleOf) { if (!_.isUndefined(multipleOf) && val % multipleOf !== 0) { throwErrorWithCode('MULTIPLE_OF', 'Not a multiple of ' + multipleOf); } }
n/a
validatePattern = function (val, pattern) { if (!_.isUndefined(pattern) && _.isNull(val.match(new RegExp(pattern)))) { throwErrorWithCode('PATTERN', 'Does not match required pattern: ' + pattern); } }
n/a
validateRequiredness = function (val, required) { if (!_.isUndefined(required) && required === true && _.isUndefined(val)) { throwErrorWithCode('REQUIRED', 'Is required'); } }
...
paramName = schema.name;
paramPath = swaggerVersion === '1.2' ?
req.swagger.operationPath.concat(['params', paramIndex.toString()]) :
parameter.path;
val = req.swagger.params[paramName].value;
// Validate requiredness
validators.validateRequiredness(val, schema.required);
// Quick return if the value is not present
if (_.isUndefined(val)) {
return oCallback();
}
validateValue(req, schema, paramPath, val, oCallback);
...
validateSchemaConstraints = function (version, schema, path, val) { var resolveSchema = function (schema) { var resolved = schema; if (resolved.schema) { path = path.concat(['schema']); resolved = resolveSchema(resolved.schema); } return resolved; }; var type = schema.type; var allowEmptyValue; if (!type) { if (!schema.schema) { if (path[path.length - 2] === 'responses') { type = 'void'; } else { type = 'object'; } } else { schema = resolveSchema(schema); type = schema.type || 'object'; } } allowEmptyValue = schema ? schema.allowEmptyValue === true : false; try { // Always perform this check even if there is no value if (type === 'array') { validateArrayType(schema); } // Default to default value if necessary if (_.isUndefined(val)) { val = version === '1.2' ? schema.defaultValue : schema.default; path = path.concat([version === '1.2' ? 'defaultValue' : 'default']); } // If there is no explicit default value, return as all validations will fail if (_.isUndefined(val)) { return; } if (type === 'array') { _.each(val, function (val, index) { try { validateSchemaConstraints(version, schema.items || {}, path.concat(index.toString()), val); } catch (err) { err.message = 'Value at index ' + index + ' ' + (err.code === 'INVALID_TYPE' ? 'is ' : '') + err.message.charAt(0).toLowerCase() + err.message.substring(1); throw err; } }); } else { validateTypeAndFormat(version, val, type, schema.format, allowEmptyValue); } // Validate enum validateEnum(val, schema.enum); // Validate maximum validateMaximum(val, schema.maximum, type, schema.exclusiveMaximum); // Validate maxItems (Swagger 2.0+) validateMaxItems(val, schema.maxItems); // Validate maxLength (Swagger 2.0+) validateMaxLength(val, schema.maxLength); // Validate maxProperties (Swagger 2.0+) validateMaxProperties(val, schema.maxProperties); // Validate minimum validateMinimum(val, schema.minimum, type, schema.exclusiveMinimum); // Validate minItems validateMinItems(val, schema.minItems); // Validate minLength (Swagger 2.0+) validateMinLength(val, schema.minLength); // Validate minProperties (Swagger 2.0+) validateMinProperties(val, schema.minProperties); // Validate multipleOf (Swagger 2.0+) validateMultipleOf(val, schema.multipleOf); // Validate pattern (Swagger 2.0+) validatePattern(val, schema.pattern); // Validate uniqueItems validateUniqueItems(val, schema.uniqueItems); } catch (err) { err.path = path; throw err; } }
...
if (!_.isUndefined(data) && data.indexOf(val) > -1) {
createErrorOrWarning('DUPLICATE_' + codeSuffix, msgPrefix + ' already defined: ' + val, path, dest);
}
};
var validateSchemaConstraints = function (documentMetadata, schema, path, results, skip) {
try {
validators.validateSchemaConstraints(documentMetadata.swaggerVersion, schema, path
, undefined);
} catch (err) {
if (!skip) {
createErrorOrWarning(err.code, err.message, err.path, results.errors);
}
}
};
...
function validateTypeAndFormat(version, val, type, format, allowEmptyValue, skipError) { var result = true; var oVal = val; // If there is an empty value and we allow empty values, the value is always valid if (allowEmptyValue === true && val === '') { return; } if (_.isArray(val)) { _.each(val, function (aVal, index) { if (!validateTypeAndFormat(version, aVal, type, format, allowEmptyValue, true)) { throwErrorWithCode('INVALID_TYPE', 'Value at index ' + index + ' is not a valid ' + type + ': ' + aVal); } }); } else { switch (type) { case 'boolean': // Coerce the value only for Swagger 1.2 if (version === '1.2' && _.isString(val)) { if (val === 'false') { val = false; } else if (val === 'true') { val = true; } } result = _.isBoolean(val); break; case 'integer': // Coerce the value only for Swagger 1.2 if (version === '1.2' && _.isString(val)) { val = Number(val); } result = _.isFinite(val) && (Math.round(val) === val); break; case 'number': // Coerce the value only for Swagger 1.2 if (version === '1.2' && _.isString(val)) { val = Number(val); } result = _.isFinite(val); break; case 'string': if (!_.isUndefined(format)) { switch (format) { case 'date': result = isValidDate(val); break; case 'date-time': result = isValidDateTime(val); break; } } break; case 'void': result = _.isUndefined(val); break; } } if (skipError) { return result; } else if (!result) { throwErrorWithCode('INVALID_TYPE', type !== 'void' ? 'Not a valid ' + (_.isUndefined(format) ? '' : format + ' ') + type + ': ' + oVal : 'Void does not allow a value'); } }
n/a
validateUniqueItems = function (val, isUnique) { if (!_.isUndefined(isUnique) && _.uniq(val).length !== val.length) { throwErrorWithCode('ARRAY_UNIQUE', 'Does not allow duplicate values: ' + val.join(', ')); } }
n/a