Skip to content

Commit d87615f

Browse files
authored
Merge pull request #740 from postmanlabs/release/v4.15.0
Release version v4.15.0
2 parents d3967c3 + 69c2ad4 commit d87615f

File tree

11 files changed

+213
-73
lines changed

11 files changed

+213
-73
lines changed

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
## [Unreleased]
44

5+
## [v4.15.0] - 2023-06-27
6+
7+
### Added
8+
9+
- Added support for usage of XML examples of type string.
10+
11+
### Fixed
12+
13+
- Fixed issue where generated collection contained request and folder in incorrect order for v2 interface.
14+
- Fixed issue where collection generation took very large time.
15+
16+
### Changed
17+
18+
- Reduced collection size by keeping maximum generated elements for array as 1 for definitions with larger schemas.
19+
520
## [v4.14.0] - 2023-06-07
621

722
### Added
@@ -573,7 +588,9 @@ Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0
573588

574589
- Base release
575590

576-
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.14.0...HEAD
591+
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.15.0...HEAD
592+
593+
[v4.15.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.14.0...v4.15.0
577594

578595
[v4.14.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.13.0...v4.14.0
579596

assets/json-schema-faker.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23585,6 +23585,8 @@ function extend() {
2358523585
data['requiredOnly'] = false;
2358623586
data['minItems'] = 0;
2358723587
data['maxItems'] = null;
23588+
data['defaultMinItems'] = 2;
23589+
data['defaultMaxItems'] = 2;
2358823590
data['maxLength'] = null;
2358923591
data['resolveJsonPath'] = false;
2359023592
data['reuseProperties'] = false;
@@ -24161,6 +24163,25 @@ function extend() {
2416124163
}
2416224164
var minItems = value.minItems;
2416324165
var maxItems = value.maxItems;
24166+
24167+
/**
24168+
* Json schema faker fakes exactly maxItems # of elements in array if present.
24169+
* Hence we're keeping maxItems as minimum and valid as possible for schema faking (to lessen faked items)
24170+
* Maximum allowed maxItems is set to 20, set by Json schema faker option.
24171+
*/
24172+
// Override minItems to defaultMinItems if no minItems present
24173+
if (typeof minItems !== 'number' && maxItems && maxItems >= optionAPI('defaultMinItems')) {
24174+
minItems = optionAPI('defaultMinItems');
24175+
}
24176+
24177+
// Override maxItems to minItems if minItems is available
24178+
if (typeof minItems === 'number' && minItems > 0) {
24179+
maxItems = minItems;
24180+
}
24181+
24182+
// If no maxItems is defined than override with defaultMaxItems
24183+
typeof maxItems !== 'number' && (maxItems = optionAPI('defaultMaxItems'));
24184+
2416424185
if (optionAPI('minItems') && minItems === undefined) {
2416524186
// fix boundaries
2416624187
minItems = !maxItems
@@ -24188,7 +24209,14 @@ function extend() {
2418824209
var element = traverseCallback(value.items || sample, itemSubpath, resolve, null, seenSchemaCache);
2418924210
items.push(element);
2419024211
}
24191-
if (value.uniqueItems) {
24212+
24213+
/**
24214+
* Below condition puts more computation load to check unique data across multiple items by
24215+
* traversing through all data and making sure it's unique.
24216+
* As such only apply unique constraint when parameter resolution is set to "example".
24217+
* As in other case, i.e. "schema", generated value for will be same anyways.
24218+
*/
24219+
if (value.uniqueItems && optionAPI('useExamplesValue')) {
2419224220
return unique(path.concat(['items']), items, value, sample, resolve, traverseCallback, seenSchemaCache);
2419324221
}
2419424222
return items;

lib/xmlSchemaFaker.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
5050
}
5151
}
5252
else if (schema.type === 'object') {
53-
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
53+
// Use mentioned example in string directly as example
54+
if (resolveTo === 'example' && typeof schemaExample === 'string') {
55+
return '\n' + schemaExample;
56+
}
57+
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
5458
const elementName = _.get(schema, 'items.xml.name', name || 'element'),
5559
fakedContent = js2xml({ [elementName]: schemaExample }, indentChar);
5660

@@ -95,7 +99,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
9599

96100
schemaItemsWithXmlProps.xml = schema.xml;
97101

98-
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
102+
// Use mentioned example in string directly as example
103+
if (resolveTo === 'example' && typeof schemaExample === 'string') {
104+
return '\n' + schemaExample;
105+
}
106+
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
99107
const fakedContent = js2xml({ [arrayElemName]: schemaExample }, indentChar);
100108

101109
contents = '\n' + indentContent(fakedContent, cIndent);

libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ let _ = require('lodash'),
3030
/**
3131
* Get all the paths sorted in desc order.
3232
*/
33-
const paths = Object.keys(openapi.paths).sort((a, b) => {
34-
return (a > b ? -1 : 1);
35-
});
33+
const paths = Object.keys(openapi.paths);
3634

3735
if (_.isEmpty(paths)) {
3836
return tree;
@@ -152,18 +150,24 @@ let _ = require('lodash'),
152150
}
153151

154152
else {
155-
tree.setNode(`path:folder:${pathIdentifier}`, {
156-
type: 'folder',
157-
meta: {
158-
name: path,
159-
path: path,
160-
pathIdentifier: pathIdentifier
161-
},
162-
data: {}
163-
});
153+
let fromNode = index === 0 ? 'root:collection' : `path:folder:${previousPathIdentified}`,
154+
toNode = `path:folder:${pathIdentifier}`;
164155

165-
tree.setEdge(index === 0 ? 'root:collection' : `path:folder:${previousPathIdentified}`,
166-
`path:folder:${pathIdentifier}`);
156+
if (!tree.hasNode(toNode)) {
157+
tree.setNode(toNode, {
158+
type: 'folder',
159+
meta: {
160+
name: path,
161+
path: path,
162+
pathIdentifier: pathIdentifier
163+
},
164+
data: {}
165+
});
166+
}
167+
168+
if (!tree.hasEdge(fromNode, toNode)) {
169+
tree.setEdge(fromNode, toNode);
170+
}
167171
}
168172
});
169173
}

libV2/schemaUtils.js

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ const schemaFaker = require('../assets/json-schema-faker'),
7272
object: '<object>'
7373
},
7474

75+
// Maximum size of schema till whch we generate 2 elements per array (50 KB)
76+
SCHEMA_SIZE_OPTIMIZATION_THRESHOLD = 50 * 1024,
77+
7578
PROPERTIES_TO_ASSIGN_ON_CASCADE = ['type', 'nullable', 'properties'],
7679
crypto = require('crypto'),
7780

@@ -591,28 +594,6 @@ let QUERYPARAM = 'query',
591594
}
592595
// If schema is of type array
593596
else if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.array) && schema.items) {
594-
/*
595-
For VALIDATION - keep minItems and maxItems properties defined by user in schema as is
596-
FOR CONVERSION -
597-
Json schema faker fakes exactly maxItems # of elements in array
598-
Hence keeping maxItems as minimum and valid as possible for schema faking (to lessen faked items)
599-
We have enforced limit to maxItems as 100, set by Json schema faker option
600-
*/
601-
if (resolveFor === CONVERSION) {
602-
// Override minItems to default (2) if no minItems present
603-
if (!_.has(schema, 'minItems') && _.has(schema, 'maxItems') && schema.maxItems >= 2) {
604-
schema.minItems = 2;
605-
}
606-
607-
// Override maxItems to minItems if minItems is available
608-
if (_.has(schema, 'minItems') && schema.minItems > 0) {
609-
schema.maxItems = schema.minItems;
610-
}
611-
612-
// If no maxItems is defined than override with default (2)
613-
!_.has(schema, 'maxItems') && (schema.maxItems = 2);
614-
}
615-
616597
schema.items = resolveSchema(context, schema.items, stack, resolveFor, _.cloneDeep(seenRef));
617598
}
618599
// Any properties to ignored should not be available in schema
@@ -771,15 +752,23 @@ let QUERYPARAM = 'query',
771752

772753
fakeSchema = (context, schema, shouldGenerateFromExample = true) => {
773754
try {
774-
let key = hash(JSON.stringify(schema)),
755+
let stringifiedSchema = typeof schema === 'object' && (JSON.stringify(schema)),
756+
key = hash(stringifiedSchema),
757+
restrictArrayItems = typeof stringifiedSchema === 'string' &&
758+
(stringifiedSchema.length > SCHEMA_SIZE_OPTIMIZATION_THRESHOLD),
775759
fakedSchema;
776760

761+
// unassign potentially larger string data after calculation as not required
762+
stringifiedSchema = null;
763+
777764
if (context.schemaFakerCache[key]) {
778765
return context.schemaFakerCache[key];
779766
}
780767

781768
schemaFaker.option({
782-
useExamplesValue: shouldGenerateFromExample
769+
useExamplesValue: shouldGenerateFromExample,
770+
defaultMinItems: restrictArrayItems ? 1 : 2,
771+
defaultMaxItems: restrictArrayItems ? 1 : 2
783772
});
784773

785774
fakedSchema = schemaFaker(schema, null, context.schemaValidationCache || {});

libV2/xmlSchemaFaker.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
5050
}
5151
}
5252
else if (schema.type === 'object') {
53-
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
53+
// Use mentioned example in string directly as example
54+
if (resolveTo === 'example' && typeof schemaExample === 'string') {
55+
return '\n' + schemaExample;
56+
}
57+
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
5458
const elementName = _.get(schema, 'items.xml.name', name || 'element'),
5559
fakedContent = js2xml({ [elementName]: schemaExample }, indentChar);
5660

@@ -95,7 +99,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
9599

96100
schemaItemsWithXmlProps.xml = schema.xml;
97101

98-
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
102+
// Use mentioned example in string directly as example
103+
if (resolveTo === 'example' && typeof schemaExample === 'string') {
104+
return '\n' + schemaExample;
105+
}
106+
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
99107
const fakedContent = js2xml({ [arrayElemName]: schemaExample }, indentChar);
100108

101109
contents = '\n' + indentContent(fakedContent, cIndent);

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.14.0",
3+
"version": "4.15.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",
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
openapi: 3.0.3
2+
info:
3+
title: My API
4+
version: 1.0.0
5+
contact: {}
6+
servers:
7+
- url: "https://api.server.test/v1"
8+
paths:
9+
/test:
10+
post:
11+
summary: /test
12+
description: /test
13+
operationId: test
14+
requestBody:
15+
content:
16+
text/xml:
17+
schema:
18+
type: array
19+
items:
20+
type: object
21+
properties:
22+
issue:
23+
type: string
24+
description: information about the issue
25+
maxLength: 150
26+
action:
27+
type: string
28+
description: what corrective action needs to be taken to resolve the issue.
29+
maxLength: 150
30+
example: |
31+
<Errors>
32+
<error>
33+
<issue>Mandatory field are missing.</issue>
34+
<action>Resend request with valid values, any one of Hello or World.</action>
35+
</error>
36+
</Errors>
37+
responses:
38+
"200":
39+
description: OK
40+
content:
41+
application/json:
42+
examples:
43+
OK:
44+
value:
45+
Data: Postman
46+
tags: []

test/unit/base.test.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('CONVERT FUNCTION TESTS ', function() {
5555
valuePropInExample = path.join(__dirname, VALID_OPENAPI_PATH, '/valuePropInExample.yaml'),
5656
petstoreParamExample = path.join(__dirname, VALID_OPENAPI_PATH, '/petstoreParamExample.yaml'),
5757
xmlrequestBody = path.join(__dirname, VALID_OPENAPI_PATH, '/xmlExample.yaml'),
58+
xmlrequestExampleBody = path.join(__dirname, VALID_OPENAPI_PATH, '/xmlExampleWithString.yaml'),
5859
queryParamWithEnumResolveAsExample =
5960
path.join(__dirname, VALID_OPENAPI_PATH, '/query_param_with_enum_resolve_as_example.json'),
6061
formDataParamDescription = path.join(__dirname, VALID_OPENAPI_PATH, '/form_data_param_description.yaml'),
@@ -1255,6 +1256,25 @@ describe('CONVERT FUNCTION TESTS ', function() {
12551256
});
12561257
});
12571258

1259+
it('Should convert xml request body with complete string example correctly', function(done) {
1260+
const openapi = fs.readFileSync(xmlrequestExampleBody, 'utf8');
1261+
Converter.convert({ type: 'string', data: openapi },
1262+
{ schemaFaker: true, requestParametersResolution: 'Example' }, (err, conversionResult) => {
1263+
expect(err).to.be.null;
1264+
expect(conversionResult.result).to.equal(true);
1265+
expect(conversionResult.output[0].data.item[0].request.body.raw)
1266+
.to.equal(`<?xml version="1.0" encoding="UTF-8"?>
1267+
<Errors>
1268+
<error>
1269+
<issue>Mandatory field are missing.</issue>
1270+
<action>Resend request with valid values, any one of Hello or World.</action>
1271+
</error>
1272+
</Errors>
1273+
`);
1274+
done();
1275+
});
1276+
});
1277+
12581278
it('[Github #518]- integer query params with enum values get default value of NaN' +
12591279
descriptionInBodyParams, function(done) {
12601280
var openapi = fs.readFileSync(queryParamWithEnumResolveAsExample, 'utf8');
@@ -1910,7 +1930,7 @@ describe('CONVERT FUNCTION TESTS ', function() {
19101930

19111931
it('Should add corresponding Accept header in collection example\'s request correctly', function(done) {
19121932
var openapi = fs.readFileSync(acceptHeaderExample, 'utf8');
1913-
Converter.convertV2({ type: 'string', data: openapi }, {},
1933+
Converter.convert({ type: 'string', data: openapi }, {},
19141934
(err, conversionResult) => {
19151935
expect(err).to.be.null;
19161936
expect(conversionResult.result).to.equal(true);
@@ -1920,8 +1940,8 @@ describe('CONVERT FUNCTION TESTS ', function() {
19201940
expect(conversionResult.output[0].data).to.have.property('item');
19211941
expect(conversionResult.output[0].data.item.length).to.equal(1);
19221942

1923-
const item1 = conversionResult.output[0].data.item[0].item[0].item[0].item[0],
1924-
item2 = conversionResult.output[0].data.item[0].item[1].item[0],
1943+
const item1 = conversionResult.output[0].data.item[0].item[1],
1944+
item2 = conversionResult.output[0].data.item[0].item[0],
19251945
acceptHeader = {
19261946
key: 'Accept',
19271947
value: 'application/json'

0 commit comments

Comments
 (0)