Skip to content

Commit

Permalink
Merge pull request #740 from postmanlabs/release/v4.15.0
Browse files Browse the repository at this point in the history
Release version v4.15.0
  • Loading branch information
VShingala committed Jun 27, 2023
2 parents d3967c3 + 69c2ad4 commit d87615f
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 73 deletions.
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

## [Unreleased]

## [v4.15.0] - 2023-06-27

### Added

- Added support for usage of XML examples of type string.

### Fixed

- Fixed issue where generated collection contained request and folder in incorrect order for v2 interface.
- Fixed issue where collection generation took very large time.

### Changed

- Reduced collection size by keeping maximum generated elements for array as 1 for definitions with larger schemas.

## [v4.14.0] - 2023-06-07

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

- Base release

[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.14.0...HEAD
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.15.0...HEAD

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

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

Expand Down
30 changes: 29 additions & 1 deletion assets/json-schema-faker.js
Original file line number Diff line number Diff line change
Expand Up @@ -23585,6 +23585,8 @@ function extend() {
data['requiredOnly'] = false;
data['minItems'] = 0;
data['maxItems'] = null;
data['defaultMinItems'] = 2;
data['defaultMaxItems'] = 2;
data['maxLength'] = null;
data['resolveJsonPath'] = false;
data['reuseProperties'] = false;
Expand Down Expand Up @@ -24161,6 +24163,25 @@ function extend() {
}
var minItems = value.minItems;
var maxItems = value.maxItems;

/**
* Json schema faker fakes exactly maxItems # of elements in array if present.
* Hence we're keeping maxItems as minimum and valid as possible for schema faking (to lessen faked items)
* Maximum allowed maxItems is set to 20, set by Json schema faker option.
*/
// Override minItems to defaultMinItems if no minItems present
if (typeof minItems !== 'number' && maxItems && maxItems >= optionAPI('defaultMinItems')) {
minItems = optionAPI('defaultMinItems');
}

// Override maxItems to minItems if minItems is available
if (typeof minItems === 'number' && minItems > 0) {
maxItems = minItems;
}

// If no maxItems is defined than override with defaultMaxItems
typeof maxItems !== 'number' && (maxItems = optionAPI('defaultMaxItems'));

if (optionAPI('minItems') && minItems === undefined) {
// fix boundaries
minItems = !maxItems
Expand Down Expand Up @@ -24188,7 +24209,14 @@ function extend() {
var element = traverseCallback(value.items || sample, itemSubpath, resolve, null, seenSchemaCache);
items.push(element);
}
if (value.uniqueItems) {

/**
* Below condition puts more computation load to check unique data across multiple items by
* traversing through all data and making sure it's unique.
* As such only apply unique constraint when parameter resolution is set to "example".
* As in other case, i.e. "schema", generated value for will be same anyways.
*/
if (value.uniqueItems && optionAPI('useExamplesValue')) {
return unique(path.concat(['items']), items, value, sample, resolve, traverseCallback, seenSchemaCache);
}
return items;
Expand Down
12 changes: 10 additions & 2 deletions lib/xmlSchemaFaker.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
}
}
else if (schema.type === 'object') {
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
// Use mentioned example in string directly as example
if (resolveTo === 'example' && typeof schemaExample === 'string') {
return '\n' + schemaExample;
}
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
const elementName = _.get(schema, 'items.xml.name', name || 'element'),
fakedContent = js2xml({ [elementName]: schemaExample }, indentChar);

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

schemaItemsWithXmlProps.xml = schema.xml;

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

contents = '\n' + indentContent(fakedContent, cIndent);
Expand Down
32 changes: 18 additions & 14 deletions libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ let _ = require('lodash'),
/**
* Get all the paths sorted in desc order.
*/
const paths = Object.keys(openapi.paths).sort((a, b) => {
return (a > b ? -1 : 1);
});
const paths = Object.keys(openapi.paths);

if (_.isEmpty(paths)) {
return tree;
Expand Down Expand Up @@ -152,18 +150,24 @@ let _ = require('lodash'),
}

else {
tree.setNode(`path:folder:${pathIdentifier}`, {
type: 'folder',
meta: {
name: path,
path: path,
pathIdentifier: pathIdentifier
},
data: {}
});
let fromNode = index === 0 ? 'root:collection' : `path:folder:${previousPathIdentified}`,
toNode = `path:folder:${pathIdentifier}`;

tree.setEdge(index === 0 ? 'root:collection' : `path:folder:${previousPathIdentified}`,
`path:folder:${pathIdentifier}`);
if (!tree.hasNode(toNode)) {
tree.setNode(toNode, {
type: 'folder',
meta: {
name: path,
path: path,
pathIdentifier: pathIdentifier
},
data: {}
});
}

if (!tree.hasEdge(fromNode, toNode)) {
tree.setEdge(fromNode, toNode);
}
}
});
}
Expand Down
37 changes: 13 additions & 24 deletions libV2/schemaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ const schemaFaker = require('../assets/json-schema-faker'),
object: '<object>'
},

// Maximum size of schema till whch we generate 2 elements per array (50 KB)
SCHEMA_SIZE_OPTIMIZATION_THRESHOLD = 50 * 1024,

PROPERTIES_TO_ASSIGN_ON_CASCADE = ['type', 'nullable', 'properties'],
crypto = require('crypto'),

Expand Down Expand Up @@ -591,28 +594,6 @@ let QUERYPARAM = 'query',
}
// If schema is of type array
else if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.array) && schema.items) {
/*
For VALIDATION - keep minItems and maxItems properties defined by user in schema as is
FOR CONVERSION -
Json schema faker fakes exactly maxItems # of elements in array
Hence keeping maxItems as minimum and valid as possible for schema faking (to lessen faked items)
We have enforced limit to maxItems as 100, set by Json schema faker option
*/
if (resolveFor === CONVERSION) {
// Override minItems to default (2) if no minItems present
if (!_.has(schema, 'minItems') && _.has(schema, 'maxItems') && schema.maxItems >= 2) {
schema.minItems = 2;
}

// Override maxItems to minItems if minItems is available
if (_.has(schema, 'minItems') && schema.minItems > 0) {
schema.maxItems = schema.minItems;
}

// If no maxItems is defined than override with default (2)
!_.has(schema, 'maxItems') && (schema.maxItems = 2);
}

schema.items = resolveSchema(context, schema.items, stack, resolveFor, _.cloneDeep(seenRef));
}
// Any properties to ignored should not be available in schema
Expand Down Expand Up @@ -771,15 +752,23 @@ let QUERYPARAM = 'query',

fakeSchema = (context, schema, shouldGenerateFromExample = true) => {
try {
let key = hash(JSON.stringify(schema)),
let stringifiedSchema = typeof schema === 'object' && (JSON.stringify(schema)),
key = hash(stringifiedSchema),
restrictArrayItems = typeof stringifiedSchema === 'string' &&
(stringifiedSchema.length > SCHEMA_SIZE_OPTIMIZATION_THRESHOLD),
fakedSchema;

// unassign potentially larger string data after calculation as not required
stringifiedSchema = null;

if (context.schemaFakerCache[key]) {
return context.schemaFakerCache[key];
}

schemaFaker.option({
useExamplesValue: shouldGenerateFromExample
useExamplesValue: shouldGenerateFromExample,
defaultMinItems: restrictArrayItems ? 1 : 2,
defaultMaxItems: restrictArrayItems ? 1 : 2
});

fakedSchema = schemaFaker(schema, null, context.schemaValidationCache || {});
Expand Down
12 changes: 10 additions & 2 deletions libV2/xmlSchemaFaker.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ function convertSchemaToXML(name, schema, attribute, indentChar, indent, resolve
}
}
else if (schema.type === 'object') {
if (resolveTo === 'example' && typeof schemaExample !== 'undefined') {
// Use mentioned example in string directly as example
if (resolveTo === 'example' && typeof schemaExample === 'string') {
return '\n' + schemaExample;
}
else if (resolveTo === 'example' && typeof schemaExample === 'object') {
const elementName = _.get(schema, 'items.xml.name', name || 'element'),
fakedContent = js2xml({ [elementName]: schemaExample }, indentChar);

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

schemaItemsWithXmlProps.xml = schema.xml;

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

contents = '\n' + indentContent(fakedContent, cIndent);
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openapi-to-postmanv2",
"version": "4.14.0",
"version": "4.15.0",
"description": "Convert a given OpenAPI specification to Postman Collection v2.0",
"homepage": "https://github.com/postmanlabs/openapi-to-postman",
"bugs": "https://github.com/postmanlabs/openapi-to-postman/issues",
Expand Down
46 changes: 46 additions & 0 deletions test/data/valid_openapi/xmlExampleWithString.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
openapi: 3.0.3
info:
title: My API
version: 1.0.0
contact: {}
servers:
- url: "https://api.server.test/v1"
paths:
/test:
post:
summary: /test
description: /test
operationId: test
requestBody:
content:
text/xml:
schema:
type: array
items:
type: object
properties:
issue:
type: string
description: information about the issue
maxLength: 150
action:
type: string
description: what corrective action needs to be taken to resolve the issue.
maxLength: 150
example: |
<Errors>
<error>
<issue>Mandatory field are missing.</issue>
<action>Resend request with valid values, any one of Hello or World.</action>
</error>
</Errors>
responses:
"200":
description: OK
content:
application/json:
examples:
OK:
value:
Data: Postman
tags: []
26 changes: 23 additions & 3 deletions test/unit/base.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('CONVERT FUNCTION TESTS ', function() {
valuePropInExample = path.join(__dirname, VALID_OPENAPI_PATH, '/valuePropInExample.yaml'),
petstoreParamExample = path.join(__dirname, VALID_OPENAPI_PATH, '/petstoreParamExample.yaml'),
xmlrequestBody = path.join(__dirname, VALID_OPENAPI_PATH, '/xmlExample.yaml'),
xmlrequestExampleBody = path.join(__dirname, VALID_OPENAPI_PATH, '/xmlExampleWithString.yaml'),
queryParamWithEnumResolveAsExample =
path.join(__dirname, VALID_OPENAPI_PATH, '/query_param_with_enum_resolve_as_example.json'),
formDataParamDescription = path.join(__dirname, VALID_OPENAPI_PATH, '/form_data_param_description.yaml'),
Expand Down Expand Up @@ -1255,6 +1256,25 @@ describe('CONVERT FUNCTION TESTS ', function() {
});
});

it('Should convert xml request body with complete string example correctly', function(done) {
const openapi = fs.readFileSync(xmlrequestExampleBody, 'utf8');
Converter.convert({ type: 'string', data: openapi },
{ schemaFaker: true, requestParametersResolution: 'Example' }, (err, conversionResult) => {
expect(err).to.be.null;
expect(conversionResult.result).to.equal(true);
expect(conversionResult.output[0].data.item[0].request.body.raw)
.to.equal(`<?xml version="1.0" encoding="UTF-8"?>
<Errors>
<error>
<issue>Mandatory field are missing.</issue>
<action>Resend request with valid values, any one of Hello or World.</action>
</error>
</Errors>
`);
done();
});
});

it('[Github #518]- integer query params with enum values get default value of NaN' +
descriptionInBodyParams, function(done) {
var openapi = fs.readFileSync(queryParamWithEnumResolveAsExample, 'utf8');
Expand Down Expand Up @@ -1910,7 +1930,7 @@ describe('CONVERT FUNCTION TESTS ', function() {

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

const item1 = conversionResult.output[0].data.item[0].item[0].item[0].item[0],
item2 = conversionResult.output[0].data.item[0].item[1].item[0],
const item1 = conversionResult.output[0].data.item[0].item[1],
item2 = conversionResult.output[0].data.item[0].item[0],
acceptHeader = {
key: 'Accept',
value: 'application/json'
Expand Down
Loading

0 comments on commit d87615f

Please sign in to comment.