зеркало из https://github.com/Azure/sway.git
Skip validations (#70)
* Skip validation for response * update z-schema to support skipping validations * Validate parameters * fix lint * Skip invalid content type also
This commit is contained in:
Родитель
9cedce73b8
Коммит
1a2f694491
|
@ -174,6 +174,10 @@ declare class Parameter {
|
|||
getValue(request: Request): ParameterValue
|
||||
}
|
||||
|
||||
declare interface ValidateOptions {
|
||||
readonly includeErrors?: string[]
|
||||
}
|
||||
|
||||
type Operation = {
|
||||
readonly operationId?: any
|
||||
method: string
|
||||
|
@ -191,8 +195,8 @@ type Operation = {
|
|||
securityDefinitions: Object
|
||||
readonly "x-ms-long-running-operation": any
|
||||
|
||||
validateRequest(request: Request): ValidationResults
|
||||
validateResponse(response: LiveResponse): ValidationResults
|
||||
validateRequest(request: Request, options?: ValidateOptions): ValidationResults
|
||||
validateResponse(response: LiveResponse, options?: ValidateOptions): ValidationResults
|
||||
getParameters(): Parameter[]
|
||||
getResponses(): Response[]
|
||||
getResponse(statusCode?: number | string): Response
|
||||
|
|
|
@ -463,10 +463,11 @@ module.exports.removeCirculars = function (obj) {
|
|||
* @param {*} value - The value to validate
|
||||
* @param {string} schemaPath - The path to sub schema with the schema that needs to be validated
|
||||
* @param {boolean} isResponse - true if the value being validated is part of a response, false otherwise
|
||||
* @param {object} options - Options to pass to the validator.
|
||||
*
|
||||
* @returns {object} Object containing the errors and warnings of the validation
|
||||
*/
|
||||
module.exports.validateAgainstSchema = function (validator, schema, value, schemaPath, isResponse) {
|
||||
module.exports.validateAgainstSchema = function (validator, schema, value, schemaPath, isResponse, options) {
|
||||
schema = _.cloneDeep(schema); // Clone the schema as z-schema alters the provided document
|
||||
|
||||
var response = {
|
||||
|
@ -476,9 +477,9 @@ module.exports.validateAgainstSchema = function (validator, schema, value, schem
|
|||
var isValid;
|
||||
|
||||
if (!schemaPath) {
|
||||
isValid = validator.validate(value, schema);
|
||||
isValid = validator.validate(value, schema, {includeErrors: options ? options.includeErrors : undefined});
|
||||
} else {
|
||||
isValid = validator.validate(value, schema, {schemaPath: schemaPath, isResponse: isResponse});
|
||||
isValid = validator.validate(value, schema, {schemaPath, isResponse, includeErrors: options ? options.includeErrors : undefined});
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
|
@ -520,7 +521,7 @@ module.exports.validateOctetStream = function (schema) {
|
|||
};
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the content type.
|
||||
|
@ -528,10 +529,15 @@ module.exports.validateOctetStream = function (schema) {
|
|||
* @param {string} contentType - The Content-Type value of the request/response
|
||||
* @param {string[]} supportedTypes - The supported (declared) Content-Type values for the request/response
|
||||
* @param {object} results - The results object to update in the event of an invalid content type
|
||||
* @param {object} options - The options for the validation
|
||||
*/
|
||||
module.exports.validateContentType = function (contentType, supportedTypes, results) {
|
||||
module.exports.validateContentType = function (contentType, supportedTypes, results, options) {
|
||||
var rawContentType = contentType;
|
||||
|
||||
if (customValidators.shouldSkipValidate(options, ['INVALID_CONTENT_TYPE'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_.isUndefined(contentType)) {
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
|
||||
contentType = contentType.split(';')[0]; // Strip the parameter(s) from the content type
|
||||
|
|
|
@ -216,7 +216,7 @@ Operation.prototype.getResponses = function () {
|
|||
*/
|
||||
Operation.prototype.getSecurity = function () {
|
||||
return this.definitionFullyResolved.security || this.pathObject.api.definitionFullyResolved.security;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates the request.
|
||||
|
@ -236,10 +236,12 @@ Operation.prototype.getSecurity = function () {
|
|||
* *(See: {@link https://nodejs.org/api/http.html#http_class_http_clientrequest})*
|
||||
*
|
||||
* @param {object} req - The http client request *(or equivalent)*
|
||||
*
|
||||
* @param {object} options - The options passed for the validation
|
||||
*
|
||||
* @returns {module:Sway~ValidationResults} The validation results
|
||||
*/
|
||||
Operation.prototype.validateRequest = function (req) {
|
||||
Operation.prototype.validateRequest = function (req, options) {
|
||||
var results = {
|
||||
errors: [],
|
||||
warnings: []
|
||||
|
@ -250,12 +252,12 @@ Operation.prototype.validateRequest = function (req) {
|
|||
// the service works fine. Going by the RFC www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1 mentioned above we would need to assume 'application/octet-stream'
|
||||
// which in most cases would not be true so it would produce a lot of noise without adding much value.
|
||||
if (this.consumes.length > 0 && helpers.getHeaderValue(req.headers, 'content-type')) {
|
||||
helpers.validateContentType(helpers.getContentType(req.headers), this.consumes, results);
|
||||
helpers.validateContentType(helpers.getContentType(req.headers), this.consumes, results, options);
|
||||
}
|
||||
|
||||
// Validate the parameters
|
||||
_.each(this.getParameters(), function (param) {
|
||||
var paramValue = param.getValue(req);
|
||||
var paramValue = param.getValue(req, options);
|
||||
var vErr;
|
||||
|
||||
if (!paramValue.valid) {
|
||||
|
@ -288,10 +290,11 @@ Operation.prototype.validateRequest = function (req) {
|
|||
* Validates the response.
|
||||
*
|
||||
* @param {module:Sway~ServerResponseWrapper} res - The response or response like object
|
||||
* @param {module:Sway~ValidateOptions} options - The options passed for the validation
|
||||
*
|
||||
* @returns {module:Sway~ValidationResults} The validation results
|
||||
*/
|
||||
Operation.prototype.validateResponse = function (res) {
|
||||
Operation.prototype.validateResponse = function (res, options) {
|
||||
var results = {
|
||||
errors: [],
|
||||
warnings: []
|
||||
|
@ -315,7 +318,7 @@ Operation.prototype.validateResponse = function (res) {
|
|||
}
|
||||
}
|
||||
|
||||
results = response.validateResponse(res);
|
||||
results = response.validateResponse(res, options);
|
||||
return results;
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ var JsonRefs = require('json-refs');
|
|||
*
|
||||
* @param {module:Sway~Parameter} parameterObject - The `Parameter` object
|
||||
* @param {*} raw - The original/raw value
|
||||
* @param {*} validateOptions - The options for the validations passed in.
|
||||
|
||||
*
|
||||
* @property {Error} error - The error(s) encountered during processing/validating the parameter value
|
||||
* @property {module:Sway~Parameter} parameterObject - The `Parameter` object
|
||||
|
@ -47,7 +49,7 @@ var JsonRefs = require('json-refs');
|
|||
*
|
||||
* @constructor
|
||||
*/
|
||||
function ParameterValue (parameterObject, raw) {
|
||||
function ParameterValue (parameterObject, raw, validateOptions) {
|
||||
var pPath = JsonRefs.pathFromPtr(parameterObject.ptr);
|
||||
var processed = false;
|
||||
var schema = parameterObject.schema;
|
||||
|
@ -57,6 +59,7 @@ function ParameterValue (parameterObject, raw) {
|
|||
|
||||
this.parameterObject = parameterObject;
|
||||
this.raw = raw;
|
||||
this.validateOptions = validateOptions || {};
|
||||
|
||||
// Use Object.defineProperty for 'value' to allow for lazy processing of the raw value
|
||||
Object.defineProperties(this, {
|
||||
|
@ -128,7 +131,7 @@ function ParameterValue (parameterObject, raw) {
|
|||
schemaObj = parameterObject.pathObject.api.definition;
|
||||
schemaObj = escapePaths(schemaObj);
|
||||
schemaPath = helpers.constructSchemaPathFromPtr(parameterObject.ptr, parameterObject.definition.schema);
|
||||
result = helpers.validateAgainstSchema(helpers.getJSONSchemaValidator(), schemaObj, value, schemaPath);
|
||||
result = helpers.validateAgainstSchema(helpers.getJSONSchemaValidator(), schemaObj, value, schemaPath, undefined, validateOptions);
|
||||
}
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
|
|
|
@ -112,12 +112,12 @@ Parameter.prototype.getSample = function () {
|
|||
* *(See: {@link https://nodejs.org/api/http.html#http_class_http_clientrequest})*
|
||||
*
|
||||
* @param {object} req - The http client request *(or equivalent)*
|
||||
*
|
||||
* @param {object} validateOptions - The options passed for the validation
|
||||
* @returns {module:Sway~ParameterValue} The parameter value object
|
||||
*
|
||||
* @throws {Error} If the `in` value of the parameter's schema is not valid or if the `req` property to retrieve the parameter is missing
|
||||
*/
|
||||
Parameter.prototype.getValue = function (req) {
|
||||
Parameter.prototype.getValue = function (req, validateOptions) {
|
||||
if (_.isUndefined(req)) {
|
||||
throw new TypeError('req is required');
|
||||
} else if (helpers.parameterLocations.indexOf(this.in) === -1) {
|
||||
|
@ -212,7 +212,7 @@ Parameter.prototype.getValue = function (req) {
|
|||
// no default
|
||||
}
|
||||
|
||||
return new ParameterValue(this, value);
|
||||
return new ParameterValue(this, value, validateOptions);
|
||||
};
|
||||
|
||||
module.exports = Parameter;
|
||||
|
|
|
@ -114,10 +114,11 @@ Response.prototype.getSample = function () {
|
|||
* Validates the response.
|
||||
*
|
||||
* @param {module:Sway~ServerResponseWrapper} res - The response or response like object
|
||||
* @param {module:Sway~ValidateOptions} options - The options passed for validation
|
||||
*
|
||||
* @returns {module:Sway~ValidationResults} The validation results
|
||||
*/
|
||||
Response.prototype.validateResponse = function (res) {
|
||||
Response.prototype.validateResponse = function (res, options) {
|
||||
var results = {
|
||||
errors: [],
|
||||
warnings: []
|
||||
|
@ -144,7 +145,7 @@ Response.prototype.validateResponse = function (res) {
|
|||
|
||||
// Validate the Content-Type except for void responses, 204 responses and 304 responses as they have no body
|
||||
if (!_.isUndefined(this.definitionFullyResolved.schema) && _.indexOf(['204', '304'], this.statusCode) === -1) {
|
||||
helpers.validateContentType(helpers.getContentType(res.headers), this.operationObject.produces, results);
|
||||
helpers.validateContentType(helpers.getContentType(res.headers), this.operationObject.produces, results, options);
|
||||
}
|
||||
|
||||
// Validate the response headers
|
||||
|
@ -184,7 +185,7 @@ Response.prototype.validateResponse = function (res) {
|
|||
// We also do not want to validate date objects because it is redundant. If we have already converted the value
|
||||
// from a string+format to a date, we know it passes schema validation.
|
||||
if (!_.isUndefined(headerValue) && !_.isDate(headerValue)) {
|
||||
hvResults = helpers.validateAgainstSchema(jsonValidator, schema, headerValue);
|
||||
hvResults = helpers.validateAgainstSchema(jsonValidator, schema, headerValue, undefined, options);
|
||||
|
||||
if (hvResults.errors.length > 0) {
|
||||
results.errors.push({
|
||||
|
@ -216,7 +217,7 @@ Response.prototype.validateResponse = function (res) {
|
|||
bvResults = helpers.validateOctetStream(this.schema);
|
||||
} else {
|
||||
// Validate other responses
|
||||
bvResults = helpers.validateAgainstSchema(jsonValidator, swaggerSpec, bodyValue, schemaPath, true);
|
||||
bvResults = helpers.validateAgainstSchema(jsonValidator, swaggerSpec, bodyValue, schemaPath, true, options);
|
||||
}
|
||||
} catch (err) {
|
||||
bvResults = {
|
||||
|
|
|
@ -4,6 +4,10 @@ var Report = require('z-schema/src/Report');
|
|||
var ZSchemaValidator = require('z-schema/src/JsonValidation');
|
||||
|
||||
function enumValidator (report, schema, json) {
|
||||
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.1.2
|
||||
if (shouldSkipValidate(this.validateOptions, ['ENUM_CASE_MISMATCH', 'ENUM_MISMATCH'])) {
|
||||
return;
|
||||
}
|
||||
var match = false;
|
||||
var idx = schema.enum.length;
|
||||
var caseInsensitiveMatch = false;
|
||||
|
@ -23,22 +27,24 @@ function enumValidator (report, schema, json) {
|
|||
}
|
||||
|
||||
areExtraEnumValuesAllowed = schema['x-ms-enum'] && schema['x-ms-enum'].modelAsString;
|
||||
if (caseInsensitiveMatch === true) {
|
||||
if (caseInsensitiveMatch === true && !shouldSkipValidate(this.validateOptions, ['ENUM_CASE_MISMATCH'])) {
|
||||
report.addCustomError(
|
||||
'ENUM_CASE_MISMATCH',
|
||||
'Enum doesn not match case for: {0}',
|
||||
'Enum does not match case for: {0}',
|
||||
[json],
|
||||
null,
|
||||
schema
|
||||
);
|
||||
} else if (match === false && !areExtraEnumValuesAllowed) {
|
||||
} else if (match === false && !areExtraEnumValuesAllowed && !shouldSkipValidate(this.validateOptions, ['ENUM_MISMATCH'])) {
|
||||
report.addError('ENUM_MISMATCH', [json], null, schema);
|
||||
}
|
||||
}
|
||||
|
||||
function requiredPropertyValidator (report, schema, json) {
|
||||
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.3.2
|
||||
|
||||
if (shouldSkipValidate(this.validateOptions, ['OBJECT_MISSING_REQUIRED_PROPERTY'])) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(typeof json === 'object' && json === Object(json) && !Array.isArray(json))
|
||||
) {
|
||||
|
@ -70,6 +76,9 @@ function requiredPropertyValidator (report, schema, json) {
|
|||
|
||||
function typeValidator (report, schema, json) {
|
||||
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2
|
||||
if (schema.type !== 'null' && shouldSkipValidate(this.validateOptions, ['INVALID_TYPE'])) {
|
||||
return;
|
||||
}
|
||||
var jsonType = whatIs(json);
|
||||
var xMsMutabilityAllowsNullType = schema['x-ms-mutability'] && schema['x-ms-mutability'].indexOf('read') === -1;
|
||||
var isResponse = this.validateOptions && this.validateOptions.isResponse;
|
||||
|
@ -172,6 +181,16 @@ function whatIs (what) {
|
|||
return to; // undefined, boolean, string, function
|
||||
}
|
||||
|
||||
function shouldSkipValidate (options, errors) {
|
||||
return options &&
|
||||
Array.isArray(options.includeErrors) &&
|
||||
options.includeErrors.length > 0 &&
|
||||
!errors.some(function (err) {
|
||||
return options.includeErrors.includes(err);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.shouldSkipValidate = shouldSkipValidate;
|
||||
module.exports.enumValidator = enumValidator;
|
||||
module.exports.requiredPropertyValidator = requiredPropertyValidator;
|
||||
module.exports.typeValidator = typeValidator;
|
||||
|
|
|
@ -193,11 +193,11 @@ function validateArrayTypeItemsExistence (api, response, schema, path) {
|
|||
}
|
||||
}
|
||||
|
||||
function validateDefaultValue (api, response, schema, path) {
|
||||
function validateDefaultValue (api, response, schema, path, options) {
|
||||
var result;
|
||||
|
||||
if (!_.isUndefined(schema.default)) {
|
||||
result = helpers.validateAgainstSchema(helpers.getJSONSchemaValidator(), schema, schema.default);
|
||||
result = helpers.validateAgainstSchema(helpers.getJSONSchemaValidator(), schema, schema.default, undefined, options);
|
||||
|
||||
_.forEach(result.errors, function (error) {
|
||||
error.path = path.concat(error.path.concat('default'));
|
||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "yasway",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.0",
|
||||
"description": "A library that simplifies Swagger integrations.",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
@ -66,19 +66,19 @@
|
|||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ts-common/json": "^0.2.0",
|
||||
"@ts-common/string-map": "^0.2.3",
|
||||
"z-schema": "^3.25.1",
|
||||
"debug": "^3.1.0",
|
||||
"@ts-common/json": "^0.3.0",
|
||||
"@ts-common/string-map": "^0.3.0",
|
||||
"z-schema": "^4.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"faker": "^4.1.0",
|
||||
"js-base64": "^2.4.8",
|
||||
"js-yaml": "^3.12.0",
|
||||
"json-refs": "^3.0.10",
|
||||
"js-base64": "^2.5.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"json-refs": "^3.0.13",
|
||||
"json-schema-faker": "^0.5.0-rc16",
|
||||
"lodash": "^4.17.10",
|
||||
"lodash": "^4.17.11",
|
||||
"native-promise-only": "^0.8.1",
|
||||
"path-to-regexp": "^1.7.0",
|
||||
"swagger-methods": "^1.0.0",
|
||||
"swagger-methods": "^1.0.8",
|
||||
"swagger-schema-official": "2.0.0-bab6bed"
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче