Skip to content

Commit 478a494

Browse files
committed
Merge branch 'develop' into fix/fixAllOfValidationIssue597
2 parents 005f12c + b8f26e2 commit 478a494

32 files changed

+3482
-65
lines changed

assets/json-schema-faker.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23782,7 +23782,7 @@ function extend() {
2378223782
var min = Math.max(params.minimum || 0, 0);
2378323783
var max = Math.min(params.maximum || Infinity, Infinity);
2378423784
min = handleExclusiveMinimum(schema, min);
23785-
max = handleExclusiveMaximum(schema, min);
23785+
max = handleExclusiveMaximum(schema, max);
2378623786
// discard out-of-bounds enumerations
2378723787
schema.enum = schema.enum.filter(function (x) {
2378823788
if (x >= min && x <= max) {

lib/common/schemaUtilsCommon.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,16 @@ module.exports = {
345345

346346
isKnownType: function(schema) {
347347
return typeof schemaTypeToJsValidator[schema.type] === 'function';
348+
},
349+
350+
getServersPathVars: function(servers) {
351+
return servers.reduce((acc, current) => {
352+
const newVarNames = current.hasOwnProperty('variables') ?
353+
Object.keys(current.variables).filter((varName) => {
354+
return !acc.includes(varName);
355+
}) :
356+
[];
357+
return [...acc, ...newVarNames];
358+
}, []);
348359
}
349360
};

lib/schemaUtils.js

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const { formatDataPath, checkIsCorrectType, isKnownType } = require('./common/sc
2828
APP_JSON = 'application/json',
2929
APP_JS = 'application/javascript',
3030
TEXT_XML = 'text/xml',
31+
APP_XML = 'application/xml',
3132
TEXT_PLAIN = 'text/plain',
3233
TEXT_HTML = 'text/html',
3334
FORM_DATA = 'multipart/form-data',
@@ -1914,6 +1915,7 @@ module.exports = {
19141915
else if (contentObj.hasOwnProperty(APP_JSON)) { bodyType = APP_JSON; }
19151916
else if (contentObj.hasOwnProperty(TEXT_HTML)) { bodyType = TEXT_HTML; }
19161917
else if (contentObj.hasOwnProperty(TEXT_PLAIN)) { bodyType = TEXT_PLAIN; }
1918+
else if (contentObj.hasOwnProperty(APP_XML)) { bodyType = APP_XML; }
19171919
else if (contentObj.hasOwnProperty(TEXT_XML)) { bodyType = TEXT_XML; }
19181920
else {
19191921
// take the first property it has
@@ -1934,12 +1936,29 @@ module.exports = {
19341936
};
19351937
}
19361938
else {
1939+
let getXmlVersionContent = (bodyContent) => {
1940+
const regExp = new RegExp('([<\\?xml]+[\\s{1,}]+[version="\\d.\\d"]+[\\sencoding="]+.{1,15}"\\?>)');
1941+
let xmlBody = bodyContent;
1942+
1943+
if (!bodyContent.match(regExp)) {
1944+
const versionContent = '<?xml version="1.0" encoding="UTF-8"?>\n';
1945+
xmlBody = versionContent + xmlBody;
1946+
}
1947+
return xmlBody;
1948+
};
1949+
19371950
bodyData = this.convertToPmBodyData(contentObj[bodyType], requestType, bodyType,
19381951
PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache);
19391952

1953+
bodyData = (bodyType === TEXT_XML || bodyType === APP_XML) ?
1954+
getXmlVersionContent(bodyData) :
1955+
bodyData;
1956+
19401957
updateOptions = {
19411958
mode: rDataMode,
1942-
raw: JSON.stringify(bodyData, null, options.indentCharacter)
1959+
raw: bodyType !== APP_JSON ?
1960+
bodyData.toString() :
1961+
JSON.stringify(bodyData, null, options.indentCharacter)
19431962
};
19441963
}
19451964

@@ -2611,11 +2630,15 @@ module.exports = {
26112630
matchedPath,
26122631
matchedPathJsonPath,
26132632
schemaPathItems = schema.paths,
2633+
pathToMatchServer,
26142634
filteredPathItemsArray = [];
26152635

26162636
// Return no matches for invalid url (if unable to decode parsed url)
26172637
try {
26182638
pathToMatch = decodeURI(parsedUrl.pathname);
2639+
if (!_.isNil(parsedUrl.hash)) {
2640+
pathToMatch += parsedUrl.hash;
2641+
}
26192642
}
26202643
catch (e) {
26212644
console.warn(
@@ -2654,9 +2677,16 @@ module.exports = {
26542677
}
26552678
return accumulator;
26562679
}, []);
2657-
2680+
let schemaMatchResult = { match: false };
26582681
// check if path and pathToMatch match (non-null)
2659-
let schemaMatchResult = this.getPostmanUrlSchemaMatchScore(pathToMatch, path, options);
2682+
// check in explicit (local defined) servers
2683+
if (pathItemObject[method.toLowerCase()].servers) {
2684+
pathToMatchServer = this.handleExplicitServersPathToMatch(pathToMatch, path);
2685+
schemaMatchResult = this.getPostmanUrlSchemaMatchScore(pathToMatchServer, path, options);
2686+
}
2687+
else {
2688+
schemaMatchResult = this.getPostmanUrlSchemaMatchScore(pathToMatch, path, options);
2689+
}
26602690
if (!schemaMatchResult.match) {
26612691
// there was no reasonable match b/w the postman path and this schema path
26622692
return true;
@@ -3322,7 +3352,7 @@ module.exports = {
33223352

33233353
/**
33243354
*
3325-
* @param {*} determinedPathVariables the key/determined-value pairs of the path variables (from Postman)
3355+
* @param {*} matchedPathData the matchedPath data
33263356
* @param {*} transactionPathPrefix the jsonpath for this validation (will be prepended to all identified mismatches)
33273357
* @param {*} schemaPath the applicable pathItem defined at the schema level
33283358
* @param {*} components the components + paths from the OAS spec that need to be used to resolve $refs
@@ -3333,7 +3363,7 @@ module.exports = {
33333363
* @returns {array} mismatches (in the callback)
33343364
*/
33353365
checkPathVariables: function (
3336-
determinedPathVariables,
3366+
matchedPathData,
33373367
transactionPathPrefix,
33383368
schemaPath,
33393369
components,
@@ -3347,7 +3377,9 @@ module.exports = {
33473377
var mismatchProperty = 'PATHVARIABLE',
33483378
// all path variables defined in this path. acc. to the spec, all path params are required
33493379
schemaPathVariables,
3350-
pmPathVariables;
3380+
pmPathVariables,
3381+
determinedPathVariables = matchedPathData.pathVariables,
3382+
unmatchedVariablesFromTransaction = matchedPathData.unmatchedVariablesFromTransaction;
33513383

33523384
if (options.validationPropertiesToIgnore.includes(mismatchProperty)) {
33533385
return callback(null, []);
@@ -3413,17 +3445,41 @@ module.exports = {
34133445
}, (err, res) => {
34143446
let mismatches = [],
34153447
mismatchObj;
3448+
const unmatchedSchemaVariableNames = determinedPathVariables.filter((pathVariable) => {
3449+
return !pathVariable._varMatched;
3450+
}).map((schemaPathVar) => {
3451+
return schemaPathVar.key;
3452+
});
34163453

34173454
if (err) {
34183455
return callback(err);
34193456
}
34203457

34213458
// go through required schemaPathVariables, and params that aren't found in the given transaction are errors
3422-
_.each(schemaPathVariables, (pathVar) => {
3459+
_.each(schemaPathVariables, (pathVar, index) => {
34233460
if (!_.find(determinedPathVariables, (param) => {
34243461
// only consider variable matching if url path variables is not allowed
34253462
return param.key === pathVar.name && (options.allowUrlPathVarMatching || param._varMatched);
34263463
})) {
3464+
let reasonCode = 'MISSING_IN_REQUEST',
3465+
reason,
3466+
actualValue,
3467+
currentUnmatchedVariableInTransaction = unmatchedVariablesFromTransaction[index],
3468+
isInvalidValue = currentUnmatchedVariableInTransaction !== undefined;
3469+
3470+
if (unmatchedSchemaVariableNames.length > 0 && isInvalidValue) {
3471+
reason = `The ${currentUnmatchedVariableInTransaction.key} path variable does not match with ` +
3472+
`path variable expected (${unmatchedSchemaVariableNames[index]}) in the schema at this position`;
3473+
actualValue = {
3474+
key: currentUnmatchedVariableInTransaction.key,
3475+
description: this.getParameterDescription(currentUnmatchedVariableInTransaction),
3476+
value: currentUnmatchedVariableInTransaction.value
3477+
};
3478+
}
3479+
else {
3480+
reason = `The required path variable "${pathVar.name}" was not found in the transaction`;
3481+
actualValue = null;
3482+
}
34273483

34283484
// assign parameter example(s) as schema examples;
34293485
this.assignParameterExamples(pathVar);
@@ -3432,14 +3488,14 @@ module.exports = {
34323488
property: mismatchProperty,
34333489
transactionJsonPath: transactionPathPrefix,
34343490
schemaJsonPath: pathVar.pathPrefix,
3435-
reasonCode: 'MISSING_IN_REQUEST',
3436-
reason: `The required path variable "${pathVar.name}" was not found in the transaction`
3491+
reasonCode,
3492+
reason
34373493
};
34383494

34393495
if (options.suggestAvailableFixes) {
34403496
mismatchObj.suggestedFix = {
34413497
key: pathVar.name,
3442-
actualValue: null,
3498+
actualValue,
34433499
suggestedValue: {
34443500
key: pathVar.name,
34453501
value: safeSchemaFaker(pathVar.schema || {}, 'example', PROCESSING_TYPE.VALIDATION,
@@ -4439,6 +4495,32 @@ module.exports = {
44394495
});
44404496
},
44414497

4498+
/**
4499+
* Takes in the postman path and the schema path
4500+
* takes from the path the number of segments present in the schema path
4501+
* and returns the last segments from the path to match in an string format
4502+
*
4503+
* @param {string} pathToMatch - parsed path (exclude host and params) from the Postman request
4504+
* @param {string} schemaPath - schema path from the OAS spec (exclude servers object)
4505+
* @returns {string} only the selected segments from the pathToMatch
4506+
*/
4507+
handleExplicitServersPathToMatch: function (pathToMatch, schemaPath) {
4508+
let pathTMatchSlice,
4509+
schemaPathArr = _.reject(schemaPath.split('/'), (segment) => {
4510+
return segment === '';
4511+
}),
4512+
schemaPathSegments = schemaPathArr.length,
4513+
pathToMatchArr = _.reject(pathToMatch.split('/'), (segment) => {
4514+
return segment === '';
4515+
}),
4516+
pathToMatchSegments = pathToMatchArr.length;
4517+
if (pathToMatchSegments < schemaPathSegments) {
4518+
return pathToMatch;
4519+
}
4520+
pathTMatchSlice = pathToMatchArr.slice(pathToMatchArr.length - schemaPathSegments, pathToMatchArr.length);
4521+
return pathTMatchSlice.join('/');
4522+
},
4523+
44424524
/**
44434525
* @param {string} postmanPath - parsed path (exclude host and params) from the Postman request
44444526
* @param {string} schemaPath - schema path from the OAS spec (exclude servers object)

lib/schemapack.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use strict';
22

3-
43
// This is the default collection name if one can't be inferred from the OpenAPI spec
54
const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
65
{ getConcreteSchemaUtils } = require('./common/versionUtils.js'),
@@ -26,7 +25,8 @@ const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
2625
// This provides the base class for
2726
// errors with the input OpenAPI spec
2827
OpenApiErr = require('./error.js'),
29-
schemaUtils = require('./schemaUtils');
28+
schemaUtils = require('./schemaUtils'),
29+
{ getServersPathVars } = require('./common/schemaUtilsCommon.js');
3030

3131
let path = require('path'),
3232
concreteUtils,
@@ -495,12 +495,29 @@ class SchemaPack {
495495
return setTimeout(() => {
496496
// 2. perform validation for each identified matchedPath (schema endpoint)
497497
return async.map(matchedPaths, (matchedPath, pathsCallback) => {
498+
const transactionPathVariables = _.get(transaction, 'request.url.variable', []),
499+
localServers = matchedPath.path.hasOwnProperty('servers') ?
500+
matchedPath.path.servers :
501+
[],
502+
serversPathVars = [...getServersPathVars(localServers), ...getServersPathVars(schema.servers)],
503+
isNotAServerPathVar = (pathVarName) => {
504+
return !serversPathVars.includes(pathVarName);
505+
};
498506

507+
matchedPath.unmatchedVariablesFromTransaction = [];
499508
// override path variable value with actual value present in transaction
500509
// as matched pathvariable contains key as value, as it is generated from url only
501510
_.forEach(matchedPath.pathVariables, (pathVar) => {
502-
let mappedPathVar = _.find(_.get(transaction, 'request.url.variable', []), (transactionPathVar) => {
503-
return transactionPathVar.key === pathVar.key;
511+
const mappedPathVar = _.find(transactionPathVariables, (transactionPathVar) => {
512+
let matched = transactionPathVar.key === pathVar.key;
513+
if (
514+
!matched &&
515+
isNotAServerPathVar(transactionPathVar.key) &&
516+
!matchedPath.unmatchedVariablesFromTransaction.includes(transactionPathVar)
517+
) {
518+
matchedPath.unmatchedVariablesFromTransaction.push(transactionPathVar);
519+
}
520+
return matched;
504521
});
505522
pathVar.value = _.get(mappedPathVar, 'value', pathVar.value);
506523
// set _varMatched flag which represents if variable was found in transaction or not
@@ -522,7 +539,7 @@ class SchemaPack {
522539
schemaUtils.checkMetadata(transaction, '$', matchedPath.path, matchedPath.name, options, cb);
523540
},
524541
path: function(cb) {
525-
schemaUtils.checkPathVariables(matchedPath.pathVariables, '$.request.url.variable', matchedPath.path,
542+
schemaUtils.checkPathVariables(matchedPath, '$.request.url.variable', matchedPath.path,
526543
componentsAndPaths, options, schemaCache, jsonSchemaDialect, cb);
527544
},
528545
queryparams: function(cb) {

lib/swaggerUtils/inputValidationSwagger.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
const _ = require('lodash');
32
module.exports = {
43

0 commit comments

Comments
 (0)