Skip to content

Commit 353e4ab

Browse files
author
Vishal Shingala
committed
Merge branch 'release/1.2.6'
2 parents d41266d + 34b9ef5 commit 353e4ab

28 files changed

+2503
-180
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
# OpenAPI-Postman Changelog
2+
#### v1.2.5 (July 21, 2020)
3+
* Fixed TypeError happening when null parameter value provided.
4+
* Fixed an issue where suggested value for mismatch did not fix corresponding mismatch upon applying.
5+
* Fixed issue where root path was not matched during validation.
6+
* Fixed an issue where transaction was not matching if path had segment with fix and variable parts during valiadtion.
7+
* Fixed issue where URL did not contain baseUrl as host when base url variables are present.
8+
* Fixed issue where collection variable generated for path variables had empty values.
9+
* Fixed issue where invalid deserialisation was happening for param style matrix.
10+
* Added validation tests to improve code coverage.
11+
212
#### v1.2.5 (July 21, 2020)
313
* Fixed incorrect variable name in schemapack.js.
414

lib/schemaUtils.js

Lines changed: 96 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,50 @@ module.exports = {
18151815
return path.match(/(\{\{[^\/\{\}]+\}\})/g);
18161816
},
18171817

1818+
/**
1819+
* Finds all the possible path variables conversion from schema path,
1820+
* and excludes path variable that are converted to collection variable
1821+
* @param {string} path Path string
1822+
* @returns {array} Array of path variables.
1823+
*/
1824+
findPathVariablesFromSchemaPath: function (path) {
1825+
// /{path}/{file}.{format}/{hello} return [ '/{path}', '/{hello}' ]
1826+
// https://regex101.com/r/aFRWQD/4
1827+
let matches = path.match(/(\/\{[^\/\{\}]+\}(?=[\/\0]|$))/g);
1828+
1829+
// remove leading '/' and starting and ending curly braces
1830+
return _.map(matches, (match) => { return match.slice(2, -1); });
1831+
},
1832+
1833+
/**
1834+
* Finds fixed parts present in path segment of collection or schema.
1835+
*
1836+
* @param {String} segment - Path segment
1837+
* @param {String} pathType - Path type (one of 'collection' / 'schema')
1838+
* @returns {Array} - Array of strings where each element is fixed part in order of their occurence
1839+
*/
1840+
getFixedPartsFromPathSegment: function (segment, pathType = 'collection') {
1841+
var tempSegment = segment,
1842+
// collection is default
1843+
varMatches = segment.match(pathType === 'schema' ? /(\{[^\/\{\}]+\})/g : /(\{\{[^\/\{\}]+\}\})/g),
1844+
fixedParts = [];
1845+
1846+
_.forEach(varMatches, (match) => {
1847+
let matchedIndex = tempSegment.indexOf(match);
1848+
1849+
// push fixed part before collection variable if present
1850+
(matchedIndex !== 0) && (fixedParts.push(tempSegment.slice(0, matchedIndex)));
1851+
1852+
// substract starting fixed and variable part from tempSegment
1853+
tempSegment = tempSegment.substr(matchedIndex + match.length);
1854+
});
1855+
1856+
// add last fixed part if present
1857+
(tempSegment.length > 0) && (fixedParts.push(tempSegment));
1858+
1859+
return fixedParts;
1860+
},
1861+
18181862
/** Separates out collection and path variables from the reqUrl
18191863
*
18201864
* @param {string} reqUrl Request Url
@@ -1874,7 +1918,7 @@ module.exports = {
18741918
reqBody = operationItem.properties.requestBody,
18751919
itemParams = operationItem.properties.parameters,
18761920
reqParams = this.getParametersForPathItem(itemParams),
1877-
baseUrl = openapi.baseUrl,
1921+
baseUrl = '{{baseUrl}}',
18781922
pathVarArray = [],
18791923
authHelper,
18801924
item,
@@ -1906,12 +1950,14 @@ module.exports = {
19061950
// Add collection variables to the variableStore.
19071951
sanitizeResult.collectionVars.forEach((element) => {
19081952
if (!variableStore[element.name]) {
1909-
variableStore[element.name] = {
1910-
id: element.name,
1911-
value: element.default || '',
1912-
description: element.description,
1913-
type: 'collection'
1914-
};
1953+
let fakedData = options.schemaFaker ?
1954+
safeSchemaFaker(element.schema || {}, options.requestParametersResolution, PROCESSING_TYPE.CONVERSION,
1955+
PARAMETER_SOURCE.REQUEST, components, SCHEMA_FORMATS.DEFAULT, options.indentCharacter, schemaCache,
1956+
options.stackLimit) : '',
1957+
convertedPathVar = _.get(this.convertParamsWithStyle(element, fakedData, PARAMETER_SOURCE.REQUEST,
1958+
components, schemaCache), '[0]', {});
1959+
1960+
variableStore[element.name] = _.assign(convertedPathVar, { id: element.name, type: 'collection' });
19151961
}
19161962
});
19171963
// accounting for the overriding of the root level and path level servers object if present at the operation level
@@ -2294,7 +2340,8 @@ module.exports = {
22942340
retVal.push({
22952341
// using path instead of operationId / sumamry since it's widely understood
22962342
name: method + ' ' + path,
2297-
path: matchedPath,
2343+
// assign path as schemaPathName property to use path in path object
2344+
path: _.assign(matchedPath, { schemaPathName: path }),
22982345
jsonPath: matchedPathJsonPath + '.' + method.toLowerCase(),
22992346
pathVariables: pathVars,
23002347
score: score
@@ -2410,8 +2457,8 @@ module.exports = {
24102457
// decide explodable params, starting value and separators between key-value and properties for serialisation
24112458
switch (style) {
24122459
case 'matrix':
2413-
isExplodable = true;
2414-
startValue = ';' + (paramSchema.type === 'object' ? '' : (paramName + _.isEmpty(paramValue) ? '' : '='));
2460+
isExplodable = paramSchema.type === 'object' || explode;
2461+
startValue = ';' + ((paramSchema.type === 'object' && explode) ? '' : (paramName + '='));
24152462
propSeparator = explode ? ';' : ',';
24162463
keyValueSeparator = explode ? '=' : ',';
24172464
break;
@@ -2475,7 +2522,7 @@ module.exports = {
24752522
};
24762523

24772524
// for invalid param object return null
2478-
if (!_.isObject(param)) {
2525+
if (!_.isObject(param) || !_.isString(paramValue)) {
24792526
return null;
24802527
}
24812528

@@ -2702,10 +2749,10 @@ module.exports = {
27022749

27032750
// for empty string _.set creates new key with empty string '', so separate handling
27042751
if (dataPath === '') {
2705-
suggestedValue = this.getSuggestedValue(fakedValue, valueToUse, ajvError);
2752+
suggestedValue = this.getSuggestedValue(fakedValue, suggestedValue, ajvError);
27062753
}
27072754
else {
2708-
_.set(suggestedValue, dataPath, this.getSuggestedValue(fakedValue, valueToUse, ajvError));
2755+
_.set(suggestedValue, dataPath, this.getSuggestedValue(fakedValue, suggestedValue, ajvError));
27092756
}
27102757
});
27112758

@@ -2794,14 +2841,18 @@ module.exports = {
27942841
var mismatchProperty = 'PATHVARIABLE',
27952842
// all path variables defined in this path. acc. to the spec, all path params are required
27962843
schemaPathVariables,
2844+
pmPathVariables,
27972845
schemaPathVar;
27982846

27992847
if (options.validationPropertiesToIgnore.includes(mismatchProperty)) {
28002848
return callback(null, []);
28012849
}
28022850

2851+
// find all schema path variables that can be present as collection path variables
2852+
pmPathVariables = this.findPathVariablesFromSchemaPath(schemaPath.schemaPathName);
28032853
schemaPathVariables = _.filter(schemaPath.parameters, (param) => {
2804-
return (param.in === 'path');
2854+
// exclude path variables stored as collection variable from being validated further
2855+
return (param.in === 'path' && _.includes(pmPathVariables, param.name));
28052856
});
28062857

28072858
async.map(determinedPathVariables, (pathVar, cb) => {
@@ -3413,6 +3464,10 @@ module.exports = {
34133464
schemaPathPrefix + '.content', _.get(schemaResponse, 'content'), mismatchProperty, options);
34143465

34153466
_.each(_.filter(schemaHeaders, (h, hName) => {
3467+
// exclude empty headers fron validation
3468+
if (_.isEmpty(h)) {
3469+
return false;
3470+
}
34163471
h.name = hName;
34173472
return h.required;
34183473
}), (header) => {
@@ -3610,6 +3665,15 @@ module.exports = {
36103665
}
36113666
});
36123667

3668+
// handle root path '/'
3669+
if (postmanPath === '/' && schemaPath === '/') {
3670+
anyMatchFound = true;
3671+
maxScoreFound = 1; // assign max possible score
3672+
matchedPathVars = []; // no path variables present
3673+
fixedMatchedSegments = 0;
3674+
variableMatchedSegments = 0;
3675+
}
3676+
36133677
if (anyMatchFound) {
36143678
return {
36153679
match: true,
@@ -3642,7 +3706,14 @@ module.exports = {
36423706
// No. of fixed segment matches between schema and postman url path
36433707
fixedMatchedSegments = 0,
36443708
// No. of variable segment matches between schema and postman url path
3645-
variableMatchedSegments = 0;
3709+
variableMatchedSegments = 0,
3710+
// checks if schema segment provided is path variable
3711+
isSchemaSegmentPathVar = (segment) => {
3712+
return segment.startsWith('{') &&
3713+
segment.endsWith('}') &&
3714+
// check that only one path variable is present as collection path variable can contain only one var
3715+
segment.indexOf('}') === segment.lastIndexOf('}');
3716+
};
36463717

36473718
if (options.strictRequestMatching && pmSuffix.length !== schemaPath.length) {
36483719
return {
@@ -3656,28 +3727,31 @@ module.exports = {
36563727
// segments match if the schemaPath segment is {..} or the postmanPathStr is :<anything> or {{anything}}
36573728
// for (let i = pmSuffix.length - 1; i >= 0; i--) {
36583729
for (let i = 0; i < minLength; i++) {
3730+
let schemaFixedParts = this.getFixedPartsFromPathSegment(schemaPath[sMax - i], 'schema'),
3731+
collectionFixedParts = this.getFixedPartsFromPathSegment(pmSuffix[pMax - i], 'collection');
3732+
36593733
if (
3660-
(schemaPath[sMax - i] === pmSuffix[pMax - i]) || // exact match
3661-
(schemaPath[sMax - i].startsWith('{') && schemaPath[sMax - i].endsWith('}')) || // schema segment is a pathVar
3734+
(_.isEqual(schemaFixedParts, collectionFixedParts)) || // exact fixed parts match
3735+
(isSchemaSegmentPathVar(schemaPath[sMax - i])) || // schema segment is a pathVar
36623736
(pmSuffix[pMax - i].startsWith(':')) || // postman segment is a pathVar
36633737
(this.isPmVariable(pmSuffix[pMax - i])) // postman segment is an env/collection var
36643738
) {
36653739

36663740
// for variable match increase variable matched segments count (used for determining order for multiple matches)
36673741
if (
3668-
(schemaPath[sMax - i].startsWith('{') && schemaPath[sMax - i].endsWith('}')) && // schema segment is a pathVar
3742+
(isSchemaSegmentPathVar(schemaPath[sMax - i])) && // schema segment is a pathVar
36693743
((pmSuffix[pMax - i].startsWith(':')) || // postman segment is a pathVar
36703744
(this.isPmVariable(pmSuffix[pMax - i]))) // postman segment is an env/collection var
36713745
) {
36723746
variableMatchedSegments++;
36733747
}
36743748
// for exact match increase fix matched segments count (used for determining order for multiple matches)
3675-
else if (schemaPath[sMax - i] === pmSuffix[pMax - i]) {
3749+
else if (_.isEqual(schemaFixedParts, collectionFixedParts)) {
36763750
fixedMatchedSegments++;
36773751
}
36783752

3679-
// add a matched path variable only if the schema one was a pathVar
3680-
if (schemaPath[sMax - i].startsWith('{') && schemaPath[sMax - i].endsWith('}')) {
3753+
// add a matched path variable only if the schema one was a pathVar and only one path variable is in segment
3754+
if (isSchemaSegmentPathVar(schemaPath[sMax - i])) {
36813755
variables.push({
36823756
key: schemaPath[sMax - i].substring(1, schemaPath[sMax - i].length - 1),
36833757
value: pmSuffix[pMax - i]
@@ -3859,7 +3933,7 @@ module.exports = {
38593933
// Not adding colloection variables for now
38603934
suggestedValue: {
38613935
request: convertedRequest,
3862-
variables
3936+
variables: _.values(variables)
38633937
}
38643938
};
38653939
}

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openapi-to-postmanv2",
3-
"version": "1.2.5",
3+
"version": "1.2.6",
44
"description": "Convert a given OpenAPI specification to Postman Collection v2.0",
55
"homepage": "https://github.com/postmanlabs/openapi-to-postman",
66
"bugs": "https://github.com/postmanlabs/openapi-to-postman/issues",
@@ -120,7 +120,7 @@
120120
"async": "3.1.0",
121121
"commander": "2.20.3",
122122
"js-yaml": "3.13.1",
123-
"lodash": "4.17.13",
123+
"lodash": "4.17.19",
124124
"oas-resolver-browser": "2.3.3",
125125
"path-browserify": "1.0.1",
126126
"postman-collection": "3.5.5",

test/data/valid_openapi/issue#133.json

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
]
5858
}
5959
},
60-
"/new/{path}.{new-path-variable}": {
60+
"/new/{path}.{new-path-variable-1}": {
6161
"get": {
6262
"summary": "List all pets",
6363
"operationId": "listPets",
@@ -72,19 +72,21 @@
7272
"required": true,
7373
"schema": {
7474
"type": "string",
75-
"pattern": "json|xml",
76-
"example": "json"
75+
"example": "send-email"
7776
}
7877
},
7978
{
80-
"name": "new-path-variable",
79+
"name": "new-path-variable-1",
8180
"in": "path",
8281
"description": "description",
8382
"required": true,
8483
"schema": {
85-
"type": "string",
86-
"pattern": "json|xml",
87-
"example": "json"
84+
"type": "object",
85+
"properties": {
86+
"R": { "type": "integer", "example": 100 },
87+
"G": { "type": "integer", "example": 200 },
88+
"B": { "type": "integer", "example": 150 }
89+
}
8890
}
8991
}
9092
]
@@ -123,7 +125,7 @@
123125
]
124126
}
125127
},
126-
"/anotherpath/{path}/{new-path-variable}.{onemore}": {
128+
"/anotherpath/{path}/{new-path-variable-2}.{onemore}": {
127129
"get": {
128130
"summary": "List all pets",
129131
"operationId": "listPets",
@@ -143,14 +145,16 @@
143145
}
144146
},
145147
{
146-
"name": "new-path-variable",
148+
"name": "new-path-variable-2",
147149
"in": "path",
148150
"description": "description",
149151
"required": true,
150152
"schema": {
151-
"type": "string",
152-
"pattern": "json|xml",
153-
"example": "json"
153+
"type": "array",
154+
"items": {
155+
"type": "string",
156+
"example": "exampleString"
157+
}
154158
}
155159
},
156160
{

test/data/valid_openapi/multiple_refs.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@
5959
"schemas": {
6060
"RequestBody": {
6161
"required": [
62-
"id",
63-
"name"
62+
"requestId",
63+
"requestName"
6464
],
6565
"properties": {
6666
"requestId": {
@@ -74,8 +74,8 @@
7474
},
7575
"ResponseBody": {
7676
"required": [
77-
"id",
78-
"name"
77+
"responseId",
78+
"responseName"
7979
],
8080
"properties": {
8181
"responseId": {

0 commit comments

Comments
 (0)