* 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:
Vlad Barosan 2019-04-05 14:14:20 -07:00 коммит произвёл GitHub
Родитель 9cedce73b8
Коммит 1a2f694491
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 74 добавлений и 38 удалений

8
index.d.ts поставляемый
Просмотреть файл

@ -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'));

Просмотреть файл

@ -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"
}
}