Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed issue causing incorrect suggested fixes for validation. #345

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 76 additions & 31 deletions lib/schemaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2810,6 +2810,43 @@ module.exports = {
return jsonContentType;
},

/**
* Converts existing JSON values of mismatch objects to serialised value based on
* serilasation params defined in schema.
*
* @param {Array} mismatches - Array of mismatch objects
* @param {Array} resolvedSchemaParams - All resolved schema params
* @param {Object} components - Components in the spec that the schema might refer to
* @param {Object} schemaCache - object storing schemaFaker and schmeResolution caches
* @param {Object} options - Global options
* @returns {Array} - Array of mismatch objects with updated value
*/
convertToSerialisedValues: function (mismatches, resolvedSchemaParams, components, schemaCache, options) {
// fetches property name from schem path
let getPropNameFromSchemPath = (schemaPath) => {
let regex = /\.properties\[(.+)\]/gm;
return _.last(regex.exec(schemaPath));
};

return _.map(mismatches, (mismatchObj) => {
if (!_.isEmpty(mismatchObj)) {
let propertyName = getPropNameFromSchemPath(mismatchObj.schemaJsonPath),
schemaParam = _.find(resolvedSchemaParams, (param) => { return param.name === propertyName; }),
serializedParamValue;

if (schemaParam) {
// serialize param value (to be used in suggested value)
serializedParamValue = _.get(this.convertParamsWithStyle(schemaParam, _.get(mismatchObj,
'suggestedFix.suggestedValue'), PARAMETER_SOURCE.REQUEST, components, schemaCache, options),
'[0].value');
_.set(mismatchObj, 'suggestedFix.actualValue', schemaParam.actualValue);
_.set(mismatchObj, 'suggestedFix.suggestedValue', serializedParamValue);
}
}
return mismatchObj;
});
},

/**
*
* @param {String} property - one of QUERYPARAM, PATHVARIABLE, HEADER, BODY, RESPONSE_HEADER, RESPONSE_BODY
Expand Down Expand Up @@ -3355,7 +3392,8 @@ module.exports = {
resolvedSchemaParams.push({
name: propName,
schema: propSchema,
isResolvedParam: true,
required: param.required || false, // treat exploded param as required if parent param is required
isResolvedParam: !_.includes(['array', 'object'], _.get(propSchema, 'type')),
pathPrefix
});
});
Expand Down Expand Up @@ -3389,10 +3427,21 @@ module.exports = {
// assign parameter example(s) as schema examples;
this.assignParameterExamples(schemaParam);

if (!schemaParam.isResolvedParam) {
if (schemaParam.isResolvedParam === false) {
// simply parse value which will be not serialised and is stringified
try {
resolvedParamValue = JSON.parse(pQuery.value);
}
catch (err) {
console.warn(`Unable to parse value for parameter ${schemaParam.name}`);
}
}
else if (!schemaParam.isResolvedParam) {
resolvedParamValue = this.deserialiseParamValue(schemaParam, pQuery.value, PARAMETER_SOURCE.REQUEST,
components, schemaCache);
}
// store existing value to be used in mismatch object
schemaParam.actualValue = pQuery.value;

// query found in spec. check query's schema
setTimeout(() => {
Expand All @@ -3414,7 +3463,7 @@ module.exports = {
let mismatches = [],
mismatchObj;

_.each(_.filter(schemaParams, (q) => { return q.required; }), (qp) => {
_.each(_.filter(resolvedSchemaParams, (q) => { return q.required; }), (qp) => {
if (!_.find(requestQueryParams, (param) => { return param.key === qp.name; })) {

// assign parameter example(s) as schema examples;
Expand Down Expand Up @@ -3443,7 +3492,11 @@ module.exports = {
mismatches.push(mismatchObj);
}
});
return callback(null, _.concat(_.flatten(res), mismatches));

mismatches = this.convertToSerialisedValues(_.concat(_.flatten(res), mismatches), resolvedSchemaParams,
components, schemaCache, options);

return callback(null, mismatches);
});
},

Expand Down Expand Up @@ -3864,7 +3917,8 @@ module.exports = {
resolvedSchemaParams.push({
name: key,
schema: value,
isResolvedParam: true
required: resolvedProp.required || false, // treat exploded param as required if parent param is required
isResolvedParam: !_.includes(['array', 'object'], _.get(propSchema, 'type'))
});
});
}
Expand Down Expand Up @@ -3894,7 +3948,16 @@ module.exports = {
return cb(null, mismatches);
}

if (!schemaParam.isResolvedParam) {
if (schemaParam.isResolvedParam === false) {
// simply parse value which will be not serialised and is stringified
try {
resolvedParamValue = JSON.parse(uParam.value);
}
catch (err) {
console.warn(`Unable to parse value for parameter ${schemaParam.name}`);
}
}
else if (!schemaParam.isResolvedParam) {
resolvedParamValue = this.deserialiseParamValue(schemaParam, uParam.value, PARAMETER_SOURCE.REQUEST,
components, schemaCache);
}
Expand All @@ -3919,30 +3982,7 @@ module.exports = {
}, 0);
}, (err, res) => {
let mismatches = [],
mismatchObj,
// fetches property name from schem path
getPropNameFromSchemPath = (schemaPath) => {
let regex = /\.properties\[(.+)\]/gm;
return _.last(regex.exec(schemaPath));
};

// update actual value and suggested value from JSON to serialized strings
_.forEach(_.flatten(res), (mismatchObj) => {
if (!_.isEmpty(mismatchObj)) {
let propertyName = getPropNameFromSchemPath(mismatchObj.schemaJsonPath),
schemaParam = _.find(resolvedSchemaParams, (param) => { return param.name === propertyName; }),
serializedParamValue;

if (schemaParam) {
// serialize param value (to be used in suggested value)
serializedParamValue = _.get(this.convertParamsWithStyle(schemaParam, _.get(mismatchObj,
'suggestedFix.suggestedValue'), PARAMETER_SOURCE.REQUEST, components, schemaCache, options),
'[0].value');
_.set(mismatchObj, 'suggestedFix.actualValue', schemaParam.actualValue);
_.set(mismatchObj, 'suggestedFix.suggestedValue', serializedParamValue);
}
}
});
mismatchObj;

_.each(resolvedSchemaParams, (uParam) => {
// report mismatches only for reuired properties
Expand Down Expand Up @@ -3970,7 +4010,12 @@ module.exports = {
mismatches.push(mismatchObj);
}
});
return callback(null, _.concat(_.flatten(res), mismatches));

// update actual value and suggested value from JSON to serialized strings
mismatches = this.convertToSerialisedValues(_.concat(_.flatten(res), mismatches), resolvedSchemaParams,
components, schemaCache, options);

return callback(null, mismatches);
});
}
else {
Expand Down
14 changes: 13 additions & 1 deletion test/data/validationData/urlencodedBodySpec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,27 @@ paths:
propSimple:
type: integer
example: 123
propObjectMissing:
type: object
properties:
prop1:
type: string
example: hola
prop2:
type: string
example: world!
required:
- status
- propObjectMissing
encoding:
propObjectExplodable:
style: form
explode: true
propObjectNonExplodable:
style: form
explode: false
propObjectMissing:
style: form
explode: false
responses:
'200':
description: Pet updated.
9 changes: 7 additions & 2 deletions test/unit/validator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -641,15 +641,15 @@ describe('VALIDATE FUNCTION TESTS ', function () {
resultObj,
historyRequest = [],
schemaPack = new Converter.SchemaPack({ type: 'string', data: urlencodedBodySpec },
{ suggestAvailableFixes: true });
{ suggestAvailableFixes: true, showMissingInSchemaErrors: true });

getAllTransactions(JSON.parse(urlencodedBodyCollection), historyRequest);

schemaPack.validateTransaction(historyRequest, (err, result) => {
expect(err).to.be.null;
expect(result).to.be.an('object');
resultObj = result.requests[historyRequest[0].id].endpoints[0];
expect(resultObj.mismatches).to.have.lengthOf(3);
expect(resultObj.mismatches).to.have.lengthOf(4);

// for explodable property of type object named "propObjectExplodable",
// second property named "prop2" is incorrect, while property "prop1" is correct
Expand All @@ -666,6 +666,11 @@ describe('VALIDATE FUNCTION TESTS ', function () {
expect(resultObj.mismatches[2].transactionJsonPath).to.eql('$.request.body.urlencoded[4].value');
expect(resultObj.mismatches[2].suggestedFix.actualValue).to.eql('999');
expect(resultObj.mismatches[2].suggestedFix.suggestedValue).to.eql('exampleString');

// property named "propObjectMissing" is missing in request
expect(resultObj.mismatches[3].reasonCode).to.eql('MISSING_IN_REQUEST');
expect(resultObj.mismatches[3].suggestedFix.key).to.eql('propObjectMissing');
expect(resultObj.mismatches[3].suggestedFix.suggestedValue).to.eql('prop3,hold,prop4,world!');
done();
});
});
Expand Down