Skip to content

Commit 0540979

Browse files
author
Dhwaneet Bhatt
authored
Merge pull request #702 from postmanlabs/release/v4.11.0
Release/v4.11.0
2 parents f94f06b + 708cd28 commit 0540979

13 files changed

+251
-28
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# OpenAPI-Postman Changelog
22

3+
## [Unreleased]
4+
5+
## [v4.11.0] - 2021-04-14
6+
7+
### Added
8+
9+
- Fixed issue [#11680](https://github.com/postmanlabs/postman-app-support/issues/11680) Added support for contentType field for Formdata request bodies.
10+
- Fixed issue [#10928](https://github.com/postmanlabs/postman-app-support/issues/10928) Added support for usage of interface version in CLI conversions with v2 as default.
11+
12+
### Fixed
13+
14+
- Fixed various known type errors related issues that were causing conversion errors.
15+
- Fixed an issue where default response was not considered correctly while validating collection request response.
16+
17+
## Previous Releases
18+
Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format.
19+
320
#### v4.10.2 (March 13, 2023)
421
* Fixed issue where Accept header was generated correctly in convertV2() interface.
522

@@ -436,3 +453,7 @@
436453

437454
#### v0.0.1 (October 23, 2018)
438455
* Base release
456+
457+
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.11.0...HEAD
458+
459+
[v4.11.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.10.2...v4.11.0

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,28 +56,31 @@ The converter can be used as a CLI tool as well. The following [command line opt
5656

5757
### Options
5858

59-
- `-s <source>`, `--spec <source>`
59+
- `-s <source>`, `--spec <source>`
6060
Used to specify the OpenAPI specification (file path) which is to be converted
6161

62-
- `-o <destination>`, `--output <destination>`
62+
- `-o <destination>`, `--output <destination>`
6363
Used to specify the destination file in which the collection is to be written
6464

65-
- `-p`, `--pretty`
65+
- `-p`, `--pretty`
6666
Used to pretty print the collection object while writing to a file
6767

68-
- `-O`, `--options`
68+
- `-i`, `--interface-version`
69+
Specifies the interface version of the converter to be used. Value can be 'v2' or 'v1'. Default is 'v2'.
70+
71+
- `-O`, `--options`
6972
Used to supply options to the converter, for complete options details see [here](/OPTIONS.md)
7073

71-
- `-c`, `--options-config`
74+
- `-c`, `--options-config`
7275
Used to supply options to the converter through config file, for complete options details see [here](/OPTIONS.md)
7376

74-
- `-t`, `--test`
77+
- `-t`, `--test`
7578
Used to test the collection with an in-built sample specification
7679

77-
- `-v`, `--version`
80+
- `-v`, `--version`
7881
Specifies the version of the converter
7982

80-
- `-h`, `--help`
83+
- `-h`, `--help`
8184
Specifies all the options along with a few usage examples on the terminal
8285

8386

bin/openapi2postmanv2.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var _ = require('lodash'),
1212
definedOptions,
1313
testFlag,
1414
swaggerInput,
15+
interfaceVersion,
1516
swaggerData;
1617

1718
/**
@@ -41,6 +42,14 @@ function parseOptions (value) {
4142
console.warn('\x1b[33m%s\x1b[0m', 'Warning: Invalid option supplied ', option[0]);
4243
}
4344
});
45+
46+
/**
47+
* As v2 interface uses parametersResolution instead of previous requestParametersResolution option,
48+
* override value of parametersResolution if it's not defined and requestParametersResolution is defined
49+
*/
50+
if (_.has(parsedOptions, 'requestParametersResolution') && !_.has(parsedOptions, 'parametersResolution')) {
51+
parsedOptions.parametersResolution = parsedOptions.requestParametersResolution;
52+
}
4453
return parsedOptions;
4554
}
4655

@@ -50,6 +59,7 @@ program
5059
.option('-o, --output <output>', 'Write the collection to an output file')
5160
.option('-t, --test', 'Test the OPENAPI converter')
5261
.option('-p, --pretty', 'Pretty print the JSON file')
62+
.option('-i, --interface-version <interfaceVersion>', 'Interface version of convert() to be used')
5363
.option('-c, --options-config <optionsConfig>', 'JSON file containing Converter options')
5464
.option('-O, --options <options>', 'comma separated list of options', parseOptions);
5565

@@ -76,6 +86,7 @@ inputFile = program.spec;
7686
outputFile = program.output || false;
7787
testFlag = program.test || false;
7888
prettyPrintFlag = program.pretty || false;
89+
interfaceVersion = program.interfaceVersion || 'v2';
7990
configFile = program.optionsConfig || false;
8091
definedOptions = (!(program.options instanceof Array) ? program.options : {});
8192
swaggerInput;
@@ -112,7 +123,8 @@ function writetoFile(prettyPrintFlag, file, collection) {
112123
* @returns {void}
113124
*/
114125
function convert(swaggerData) {
115-
let options = {};
126+
let options = {},
127+
convertFn = interfaceVersion === 'v1' ? 'convert' : 'convertV2';
116128

117129
// apply options from config file if present
118130
if (configFile) {
@@ -126,7 +138,7 @@ function convert(swaggerData) {
126138
options = definedOptions;
127139
}
128140

129-
Converter.convert({
141+
Converter[convertFn]({
130142
type: 'string',
131143
data: swaggerData
132144
}, options, (err, status) => {

lib/schemaUtils.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,7 @@ module.exports = {
15131513
var example,
15141514
exampleKey;
15151515

1516-
if (typeof exampleObj !== 'object') {
1516+
if (!exampleObj || typeof exampleObj !== 'object') {
15171517
return '';
15181518
}
15191519

@@ -1720,7 +1720,7 @@ module.exports = {
17201720

17211721
Object.keys(deepObject).forEach((key) => {
17221722
let value = deepObject[key];
1723-
if (typeof value === 'object') {
1723+
if (value && typeof value === 'object') {
17241724
extractedParams = _.concat(extractedParams, this.extractDeepObjectParams(value, objectKey + '[' + key + ']'));
17251725
}
17261726
else {
@@ -1896,6 +1896,7 @@ module.exports = {
18961896
updateOptions = {},
18971897
reqBody = new sdk.RequestBody(),
18981898
contentHeader,
1899+
contentTypes = {},
18991900
rDataMode,
19001901
params,
19011902
encoding,
@@ -2018,6 +2019,10 @@ module.exports = {
20182019
REQUEST_TYPE.ROOT, PARAMETER_SOURCE.REQUEST, components, options, schemaCache));
20192020
}
20202021
});
2022+
2023+
if (typeof _.get(encoding, `[${key}].contentType`) === 'string') {
2024+
contentTypes[key] = encoding[key].contentType;
2025+
}
20212026
}
20222027
// Collection v2.1 schema allows form param value to be only string
20232028
if (typeof value !== 'string') {
@@ -2055,6 +2060,9 @@ module.exports = {
20552060
});
20562061
}
20572062
param.description = description;
2063+
if (contentTypes[key]) {
2064+
param.contentType = contentTypes[key];
2065+
}
20582066
paramArray.push(param);
20592067
});
20602068
updateOptions = {
@@ -2640,7 +2648,7 @@ module.exports = {
26402648
// App / Collection transformer fail with the object syntax
26412649
if (item.request.url.variables.members && item.request.url.variables.members.length > 0) {
26422650
item.request.url.variables.members = _.map(item.request.url.variables.members, (m) => {
2643-
if (typeof m.description === 'object' && m.description.content) {
2651+
if (m.description && typeof m.description === 'object' && m.description.content) {
26442652
m.description = m.description.content;
26452653
}
26462654
return m;

libV2/schemaUtils.js

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,19 @@ let QUERYPARAM = 'query',
236236
return { collectionVariables, pathVariables, baseUrl };
237237
},
238238

239+
/**
240+
* Provides ref stack limit for current instance
241+
* @param {*} stackLimit - Defined stackLimit in options
242+
*
243+
* @returns {Number} Returns the stackLimit to be used
244+
*/
245+
getRefStackLimit = (stackLimit) => {
246+
if (typeof stackLimit === 'number' && stackLimit > REF_STACK_LIMIT) {
247+
return stackLimit;
248+
}
249+
return REF_STACK_LIMIT;
250+
},
251+
239252
/**
240253
* Resolve a given ref from the schema
241254
* @param {Object} context - Global context object
@@ -246,9 +259,10 @@ let QUERYPARAM = 'query',
246259
* @returns {Object} Returns the object that staisfies the schema
247260
*/
248261
resolveRefFromSchema = (context, $ref, stackDepth = 0, seenRef = {}) => {
249-
const { specComponents } = context;
262+
const { specComponents } = context,
263+
{ stackLimit } = context.computedOptions;
250264

251-
if (stackDepth >= REF_STACK_LIMIT) {
265+
if (stackDepth >= getRefStackLimit(stackLimit)) {
252266
return { value: ERR_TOO_MANY_LEVELS };
253267
}
254268

@@ -315,9 +329,10 @@ let QUERYPARAM = 'query',
315329
* @returns {Object} Returns the object that staisfies the schema
316330
*/
317331
resolveRefForExamples = (context, $ref, stackDepth = 0, seenRef = {}) => {
318-
const { specComponents } = context;
332+
const { specComponents } = context,
333+
{ stackLimit } = context.computedOptions;
319334

320-
if (stackDepth >= REF_STACK_LIMIT) {
335+
if (stackDepth >= getRefStackLimit(stackLimit)) {
321336
return { value: ERR_TOO_MANY_LEVELS };
322337
}
323338

@@ -401,7 +416,7 @@ let QUERYPARAM = 'query',
401416
let example = {},
402417
exampleKey;
403418

404-
if (typeof exampleObj !== 'object') {
419+
if (!exampleObj || typeof exampleObj !== 'object') {
405420
return '';
406421
}
407422

@@ -467,12 +482,15 @@ let QUERYPARAM = 'query',
467482
return new Error('Schema is empty');
468483
}
469484

470-
if (stack >= REF_STACK_LIMIT) {
485+
const { stackLimit } = context.computedOptions;
486+
487+
if (stack >= getRefStackLimit(stackLimit)) {
471488
return { value: ERR_TOO_MANY_LEVELS };
472489
}
473490

474491
stack++;
475492

493+
// eslint-disable-next-line one-var
476494
const compositeKeyword = schema.anyOf ? 'anyOf' : 'oneOf',
477495
{ concreteUtils } = context;
478496

@@ -545,6 +563,11 @@ let QUERYPARAM = 'query',
545563
{ includeDeprecated } = context.computedOptions;
546564

547565
_.forOwn(schema.properties, (property, propertyName) => {
566+
// Skip property resolution if it's not schema object
567+
if (!_.isObject(property)) {
568+
return;
569+
}
570+
548571
if (
549572
property.format === 'decimal' ||
550573
property.format === 'byte' ||
@@ -875,7 +898,7 @@ let QUERYPARAM = 'query',
875898

876899
Object.keys(deepObject).forEach((key) => {
877900
let value = deepObject[key];
878-
if (typeof value === 'object') {
901+
if (value && typeof value === 'object') {
879902
extractedParams = _.concat(extractedParams, extractDeepObjectParams(value, objectKey + '[' + key + ']'));
880903
}
881904
else {
@@ -1211,6 +1234,7 @@ let QUERYPARAM = 'query',
12111234
resolveFormDataRequestBodyForPostmanRequest = (context, requestBodyContent) => {
12121235
let bodyData = '',
12131236
formDataParams = [],
1237+
encoding = {},
12141238
requestBodyData = {
12151239
mode: 'formdata',
12161240
formdata: formDataParams
@@ -1221,9 +1245,11 @@ let QUERYPARAM = 'query',
12211245
}
12221246

12231247
bodyData = resolveRequestBodyData(context, requestBodyContent.schema);
1248+
encoding = _.get(requestBodyContent, 'encoding', {});
12241249

12251250
_.forOwn(bodyData, (value, key) => {
12261251
let requestBodySchema,
1252+
contentType = null,
12271253
paramSchema,
12281254
description,
12291255
param;
@@ -1240,6 +1266,10 @@ let QUERYPARAM = 'query',
12401266
_.indexOf(requestBodySchema.required, key) !== -1;
12411267
description = getParameterDescription(paramSchema);
12421268

1269+
if (typeof _.get(encoding, `[${key}].contentType`) === 'string') {
1270+
contentType = encoding[key].contentType;
1271+
}
1272+
12431273
// TODO: Add handling for headers from encoding
12441274

12451275
if (paramSchema && paramSchema.type === 'binary') {
@@ -1258,6 +1288,9 @@ let QUERYPARAM = 'query',
12581288
}
12591289

12601290
param.description = description;
1291+
if (contentType) {
1292+
param.contentType = contentType;
1293+
}
12611294

12621295
formDataParams.push(param);
12631296
});
@@ -1720,8 +1753,9 @@ let QUERYPARAM = 'query',
17201753
let responses = [],
17211754
requestAcceptHeader;
17221755

1723-
_.forOwn(operationItem.responses, (responseSchema, code) => {
1756+
_.forOwn(operationItem.responses, (responseObj, code) => {
17241757
let response,
1758+
responseSchema = _.has(responseObj, '$ref') ? resolveSchema(context, responseObj) : responseObj,
17251759
{ includeAuthInfoInExample } = context.computedOptions,
17261760
responseAuthHelper,
17271761
auth = request.auth,

libV2/validationUtils.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ function extractDeepObjectParams (deepObject, objectKey) {
885885

886886
Object.keys(deepObject).forEach((key) => {
887887
let value = deepObject[key];
888-
if (typeof value === 'object') {
888+
if (value && typeof value === 'object') {
889889
extractedParams = _.concat(extractedParams, extractDeepObjectParams(value, objectKey + '[' + key + ']'));
890890
}
891891
else {
@@ -2371,7 +2371,7 @@ function checkResponses (context, transaction, transactionPathPrefix, schemaPath
23712371
// for each response, find the appropriate response from schemaPath, and then validate response body and headers
23722372
async.map(responses, (response, responseCallback) => {
23732373
let thisResponseCode = _.toString(response.code),
2374-
thisSchemaResponse = _.get(schemaPath, ['responses', thisResponseCode]),
2374+
thisSchemaResponse = _.get(schemaPath, ['responses', thisResponseCode], _.get(schemaPath, 'responses.default')),
23752375
responsePathPrefix = thisResponseCode;
23762376

23772377
// X can be used as wild card character, so response code like 2XX in definition are valid
@@ -2449,6 +2449,8 @@ function checkResponses (context, transaction, transactionPathPrefix, schemaPath
24492449
missingResponses = [];
24502450

24512451
_.each(_.get(schemaPath, 'responses'), (responseObj, responseCode) => {
2452+
responseCode = responseCode === 'default' ? '500' : responseCode;
2453+
24522454
if (!_.includes(matchedResponses, responseCode)) {
24532455
let mismatchObj = {
24542456
property: 'RESPONSE',

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openapi-to-postmanv2",
3-
"version": "4.10.2",
3+
"version": "4.11.0",
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",

test/data/valid_openapi/required_in_parameters.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@
9898
"type": "string"
9999
}
100100
}
101+
},
102+
"encoding": {
103+
"formParam1": {
104+
"contentType": "application/xml"
105+
},
106+
"formParam2": {
107+
"contentType": "application/js"
108+
}
101109
}
102110
}
103111
}

0 commit comments

Comments
 (0)