support for defined schema dialect property on validate transaction

This commit is contained in:
Luis Tejeda
2022-02-03 12:24:50 -06:00
parent 8c2cff19e7
commit e2d324aa4a
4 changed files with 78 additions and 30 deletions

View File

@@ -30,11 +30,22 @@ function getLocalDraft(schema) {
/**
* Gets the correct validator according to the draft
*
* @param {string} draft - the draft identifier
* @returns {string} the id
* @param {string} draftToUse - the draft to use in validation
* @returns {string} the draft identifier
*/
function getAjvValidator(draft) {
return draft === specialDraft ? validateSchemaAJVDraft04 : validateSchemaAJV;
function getAjvValidator(draftToUse) {
return draftToUse === specialDraft ? validateSchemaAJVDraft04 : validateSchemaAJV;
}
/**
* Defines the draft to use in validation
*
* @param {string} localDraft - the draft from the schema object
* @param {string} jsonSchemaDialect - the draft from the OAS object
* @returns {string} the draft to use
*/
function getDraftToUse(localDraft, jsonSchemaDialect) {
return localDraft ? localDraft : jsonSchemaDialect;
}
/**
@@ -44,13 +55,15 @@ function getAjvValidator(draft) {
* @param {*} schema - schema to validate
* @param {*} valueToUse - value to validate schema against
* @param {*} options - a standard list of options that's globally passed around. Check options.js for more.
* @param {string} jsonSchemaDialect - the defined schema in the OAS object
* @returns {*} - Found Validation Errors
*/
function validateSchema (schema, valueToUse, options = {}) {
function validateSchema (schema, valueToUse, options = {}, jsonSchemaDialect) {
let validate,
compoundResult,
filteredValidationError,
draftToUse = getLocalDraft(schema);
localDraft = getLocalDraft(schema),
draftToUse = getDraftToUse(localDraft, jsonSchemaDialect);
const validator = getAjvValidator(draftToUse);
compoundResult = validator(schema, valueToUse, draftToUse);
if (compoundResult.filteredValidationError) {
@@ -96,5 +109,6 @@ function validateSchema (schema, valueToUse, options = {}) {
module.exports = {
validateSchema,
getLocalDraft,
getAjvValidator
getAjvValidator,
getDraftToUse
};

View File

@@ -3037,11 +3037,12 @@ module.exports = {
* @param {Object} components - Components in the spec that the schema might refer to
* @param {Object} options - Global options
* @param {Object} schemaCache object storing schemaFaker and schmeResolution caches
* @param {string} jsonSchemaDialect The schema dialect defined in the OAS object
* @param {Function} callback - For return
* @returns {Array} array of mismatches
*/
checkValueAgainstSchema: function (property, jsonPathPrefix, txnParamName, value, schemaPathPrefix, openApiSchemaObj,
parameterSourceOption, components, options, schemaCache, callback) {
parameterSourceOption, components, options, schemaCache, jsonSchemaDialect, callback) {
let mismatches = [],
jsonValue,
@@ -3075,7 +3076,7 @@ module.exports = {
setTimeout(() => {
this.checkValueAgainstSchema(property, jsonPathPrefix, txnParamName, value,
`${schemaPathPrefix}.${schema.oneOf ? 'oneOf' : 'anyOf'}[${_.findIndex(compositeSchema, elementSchema)}]`,
elementSchema, parameterSourceOption, components, options, schemaCache, cb);
elementSchema, parameterSourceOption, components, options, schemaCache, jsonSchemaDialect, cb);
}, 0);
}, (err, results) => {
let sortedResults;
@@ -3167,7 +3168,7 @@ module.exports = {
// only do AJV if type is array or object
// simpler cases are handled by a type check
if (isCorrectType && needJsonMatching) {
let filteredValidationError = validateSchema(schema, valueToUse, options);
let filteredValidationError = validateSchema(schema, valueToUse, options, jsonSchemaDialect);
if (!_.isEmpty(filteredValidationError)) {
let mismatchObj,
@@ -3312,6 +3313,7 @@ module.exports = {
* @param {*} components the components + paths from the OAS spec that need to be used to resolve $refs
* @param {*} options OAS options
* @param {*} schemaCache object storing schemaFaker and schmeResolution caches
* @param {string} jsonSchemaDialect Defined schema dialect at the OAS object
* @param {*} callback Callback
* @returns {array} mismatches (in the callback)
*/
@@ -3322,6 +3324,7 @@ module.exports = {
components,
options,
schemaCache,
jsonSchemaDialect,
callback) {
// schema path should have all parameters needed
@@ -3390,7 +3393,7 @@ module.exports = {
schemaPathVar.pathPrefix + '[?(@.name==\'' + schemaPathVar.name + '\')]',
schemaPathVar.schema,
PARAMETER_SOURCE.REQUEST,
components, options, schemaCache, cb);
components, options, schemaCache, jsonSchemaDialect, cb);
}, 0);
}, (err, res) => {
let mismatches = [],
@@ -3540,7 +3543,7 @@ module.exports = {
},
checkQueryParams(requestUrl, transactionPathPrefix, schemaPath, components, options,
schemaCache, callback) {
schemaCache, jsonSchemaDialect, callback) {
let parsedUrl = require('url').parse(requestUrl),
schemaParams = _.filter(schemaPath.parameters, (param) => { return param.in === 'query'; }),
requestQueryArray = [],
@@ -3682,7 +3685,7 @@ module.exports = {
schemaParam.pathPrefix + '[?(@.name==\'' + schemaParam.name + '\')]',
schemaParam.schema,
PARAMETER_SOURCE.REQUEST,
components, options, schemaCache, cb
components, options, schemaCache, jsonSchemaDialect, cb
);
}, 0);
}, (err, res) => {
@@ -3846,7 +3849,7 @@ module.exports = {
},
checkRequestHeaders: function (headers, transactionPathPrefix, schemaPathPrefix, schemaPath,
components, options, schemaCache, callback) {
components, options, schemaCache, jsonSchemaDialect, callback) {
let schemaHeaders = _.filter(schemaPath.parameters, (param) => { return param.in === 'header'; }),
// key name of headers which are added by security schemes
securityHeaders = _.map(this.getSecurityParams(_.get(components, 'components'), 'header'), 'name'),
@@ -3904,7 +3907,7 @@ module.exports = {
schemaHeader.pathPrefix + '[?(@.name==\'' + schemaHeader.name + '\')]',
schemaHeader.schema,
PARAMETER_SOURCE.REQUEST,
components, options, schemaCache, cb
components, options, schemaCache, jsonSchemaDialect, cb
);
}, 0);
}, (err, res) => {
@@ -3962,7 +3965,7 @@ module.exports = {
},
checkResponseHeaders: function (schemaResponse, headers, transactionPathPrefix, schemaPathPrefix,
components, options, schemaCache, callback) {
components, options, schemaCache, jsonSchemaDialect, callback) {
// 0. Need to find relevant response from schemaPath.responses
let schemaHeaders,
// filter out headers which need explicit handling according to schema (other than parameters object)
@@ -4018,7 +4021,7 @@ module.exports = {
schemaPathPrefix + '.headers[' + pHeader.key + ']',
schemaHeader.schema,
PARAMETER_SOURCE.RESPONSE,
components, options, schemaCache, cb
components, options, schemaCache, jsonSchemaDialect, cb
);
}, 0);
}, (err, res) => {
@@ -4071,7 +4074,7 @@ module.exports = {
// Only application/json and application/x-www-form-urlencoded is validated for now
checkRequestBody: function (requestBody, transactionPathPrefix, schemaPathPrefix, schemaPath,
components, options, schemaCache, callback) {
components, options, schemaCache, jsonSchemaDialect, callback) {
// check for body modes
let jsonSchemaBody,
jsonContentType,
@@ -4102,6 +4105,7 @@ module.exports = {
components,
_.extend({}, options, { shortValidationErrors: true }),
schemaCache,
jsonSchemaDialect,
callback
);
}, 0);
@@ -4220,7 +4224,7 @@ module.exports = {
pathPrefix + '.properties[' + schemaParam.name + ']',
schemaParam.schema,
PARAMETER_SOURCE.REQUEST,
components, options, schemaCache, cb
components, options, schemaCache, jsonSchemaDialect, cb
);
}, 0);
}, (err, res) => {
@@ -4286,7 +4290,7 @@ module.exports = {
},
checkResponseBody: function (schemaResponse, body, transactionPathPrefix, schemaPathPrefix,
components, options, schemaCache, callback) {
components, options, schemaCache, jsonSchemaDialect, callback) {
let schemaContent,
jsonContentType,
mismatchProperty = 'RESPONSE_BODY';
@@ -4324,13 +4328,14 @@ module.exports = {
components,
_.extend({}, options, { shortValidationErrors: true }),
schemaCache,
jsonSchemaDialect,
callback
);
}, 0);
},
checkResponses: function (responses, transactionPathPrefix, schemaPathPrefix, schemaPath,
components, options, schemaCache, cb) {
components, options, schemaCache, jsonSchemaDialect, cb) {
// responses is an array of repsonses recd. for one Postman request
// we've already determined the schemaPath against which all responses need to be validated
// loop through all responses
@@ -4369,13 +4374,15 @@ module.exports = {
headers: (cb) => {
this.checkResponseHeaders(thisSchemaResponse, response.header,
transactionPathPrefix + '[' + response.id + '].header',
schemaPathPrefix + '.responses.' + responsePathPrefix, components, options, schemaCache, cb);
schemaPathPrefix + '.responses.' + responsePathPrefix,
components, options, schemaCache, jsonSchemaDialect, cb);
},
body: (cb) => {
// assume it's JSON at this point
this.checkResponseBody(thisSchemaResponse, response.body,
transactionPathPrefix + '[' + response.id + '].body',
schemaPathPrefix + '.responses.' + responsePathPrefix, components, options, schemaCache, cb);
schemaPathPrefix + '.responses.' + responsePathPrefix,
components, options, schemaCache, jsonSchemaDialect, cb);
}
}, (err, result) => {
return responseCallback(null, {

View File

@@ -378,7 +378,8 @@ class SchemaPack {
schemaResolutionCache: this.schemaResolutionCache,
schemaFakerCache: this.schemaFakerCache
},
matchedEndpoints = [];
matchedEndpoints = [],
jsonSchemaDialect = schema.jsonSchemaDialect;
// Only change the stack limit if the optimizeConversion option is true
if (options.optimizeConversion) {
@@ -498,23 +499,23 @@ class SchemaPack {
},
path: function(cb) {
schemaUtils.checkPathVariables(matchedPath.pathVariables, '$.request.url.variable', matchedPath.path,
componentsAndPaths, options, schemaCache, cb);
componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
},
queryparams: function(cb) {
schemaUtils.checkQueryParams(requestUrl, '$.request.url.query', matchedPath.path,
componentsAndPaths, options, schemaCache, cb);
componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
},
headers: function(cb) {
schemaUtils.checkRequestHeaders(transaction.request.header, '$.request.header', matchedPath.jsonPath,
matchedPath.path, componentsAndPaths, options, schemaCache, cb);
matchedPath.path, componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
},
requestBody: function(cb) {
schemaUtils.checkRequestBody(transaction.request.body, '$.request.body', matchedPath.jsonPath,
matchedPath.path, componentsAndPaths, options, schemaCache, cb);
matchedPath.path, componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
},
responses: function (cb) {
schemaUtils.checkResponses(transaction.response, '$.responses', matchedPath.jsonPath,
matchedPath.path, componentsAndPaths, options, schemaCache, cb);
matchedPath.path, componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
}
}, (err, result) => {
let allMismatches = _.concat(result.metadata, result.queryparams, result.headers, result.path,

View File

@@ -1,4 +1,7 @@
const { getLocalDraft, getAjvValidator, validateSchema } = require('../../lib/ajValidation/ajvValidation'),
const { getLocalDraft,
getAjvValidator,
validateSchema,
getDraftToUse } = require('../../lib/ajValidation/ajvValidation'),
{ validateSchemaAJVDraft04 } = require('../../lib/ajValidation/ajvValidatorDraft04'),
expect = require('chai').expect;
@@ -300,3 +303,26 @@ describe('validateSchema', function () {
expect(result.filteredValidationError).to.be.undefined;
});
});
describe('getDraftToUse', function() {
it('should return the ajv draft 04 when $schema undefined and jsonSchemaDialect is the 04', function() {
let draftToUse = getDraftToUse(undefined, 'http://json-schema.org/draft-04/schema#');
expect(draftToUse).to.equal('http://json-schema.org/draft-04/schema#');
});
it('should return the ajv draft 06 when $schema is 06 and jsonSchemaDialect is the 04', function() {
let draftToUse = getDraftToUse('http://json-schema.org/draft-06/schema#',
'http://json-schema.org/draft-04/schema#');
expect(draftToUse).to.equal('http://json-schema.org/draft-06/schema#');
});
it('should return the ajv draft 06 when $schema is 06 and jsonSchemaDialect is undefined', function() {
let draftToUse = getDraftToUse('http://json-schema.org/draft-06/schema#', undefined);
expect(draftToUse).to.equal('http://json-schema.org/draft-06/schema#');
});
it('should return undefined when $schema and jsonSchemaDialect are undefined', function() {
let draftToUse = getDraftToUse(undefined, undefined);
expect(draftToUse).to.equal(undefined);
});
});