Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
306865a
Merge pull request #865 from postmanlabs/release/v5.1.0
barshan23 Sep 1, 2025
5f6d275
Enhance type fetching to support composite schemas (anyOf, oneOf, al…
barshan23 Sep 8, 2025
77a09f2
Refactor type fetching logic to process original schema before modifi…
barshan23 Sep 9, 2025
95919aa
Update parameter resolution logic to handle 'PROCESSING' state in sch…
barshan23 Sep 9, 2025
be0a14a
Refactor schema processing to conditionally push resolved schema type…
barshan23 Sep 10, 2025
dbd1b68
Add support for composite schemas (anyOf, oneOf, allOf) in conversion…
barshan23 Sep 16, 2025
013509f
schema changes for 2-way-sync
AyushShri Sep 16, 2025
5fec948
fixed test
AyushShri Sep 16, 2025
43124e0
Enhance composite schema tests to validate specific structures for an…
barshan23 Sep 17, 2025
5d99774
Merge pull request #866 from postmanlabs/feature/add-composite-schema…
barshan23 Sep 17, 2025
97dbc8e
removed examples
AyushShri Sep 17, 2025
8e399c7
Refactor ESLint rules and remove unnecessary one-var disables; enhanc…
barshan23 Sep 18, 2025
cc01ca4
Merge branch 'develop' of github.com:postmanlabs/openapi-to-postman i…
AyushShri Sep 18, 2025
b69b0e6
added deprecated example
AyushShri Sep 18, 2025
86bb4f8
added test for description
AyushShri Sep 19, 2025
8beb3de
Resolved comments
AyushShri Sep 19, 2025
11f6dde
Bug Fixes
AyushShri Sep 19, 2025
2dd4b56
refactored
AyushShri Sep 19, 2025
16a2147
Merge pull request #867 from postmanlabs/feature/2-way-sync-schema-ch…
barshan23 Sep 19, 2025
db1969f
Prepare release v5.2.0
web-flow Sep 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@
"no-unneeded-ternary": "error",
"no-whitespace-before-property": "error",
"object-curly-spacing": ["error", "always"],
"one-var": ["error", "always"],
"one-var-declaration-per-line": "error",
"operator-assignment": "error",
"operator-linebreak": ["error", "after"],
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [Unreleased]

## [v5.2.0] - 2025-09-19

## [v5.1.0] - 2025-09-01

## [v5.0.2] - 2025-08-25
Expand Down Expand Up @@ -661,7 +663,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/v5.1.0...HEAD
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.2.0...HEAD

[v5.2.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.1.0...v5.2.0

[v5.1.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.0.2...v5.1.0

Expand Down
1 change: 0 additions & 1 deletion libV2/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable one-var */
const _ = require('lodash'),
{ Collection } = require('postman-collection/lib/collection/collection'),
GraphLib = require('graphlib'),
Expand Down
174 changes: 140 additions & 34 deletions libV2/schemaUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ schemaFaker.option({

let QUERYPARAM = 'query',
CONVERSION = 'conversion',
TYPES_GENERATION = 'typesGeneration',
HEADER = 'header',
PATHPARAM = 'path',
SCHEMA_TYPES = {
Expand Down Expand Up @@ -493,6 +494,30 @@ let QUERYPARAM = 'query',
* @returns {Object} Resolved schema
*/
resolveAllOfSchema = (context, schema, stack = 0, resolveFor = CONVERSION, seenRef = {}, currentPath = '') => {
/*
For TYPES_GENERATION, we do not want to merge the allOf schemas
instead we want to keep them separate so that we can generate types like:
allOf: [
{ $ref: '#/components/schemas/User' },
{
type: 'object',
properties: {
timestamp: { type: 'string', format: 'date-time' }
}
}
]
If we merge the schemas, we will loose the information that the schema was
a combination of multiple schemas
*/
if (resolveFor === TYPES_GENERATION) {
return {
allOf: _.map(schema.allOf, (schema) => {
// eslint-disable-next-line no-use-before-define
return _resolveSchema(context, schema, stack, resolveFor, _.cloneDeep(seenRef), currentPath);
})
};
}

try {
return mergeAllOf(_.assign(schema, {
allOf: _.map(schema.allOf, (schema) => {
Expand Down Expand Up @@ -543,7 +568,6 @@ let QUERYPARAM = 'query',

stack++;

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

Expand Down Expand Up @@ -618,7 +642,6 @@ let QUERYPARAM = 'query',
writeOnlyPropCache: context.writeOnlyPropCache
};

// eslint-disable-next-line one-var
const newReadPropCache = context.readOnlyPropCache,
newWritePropCache = context.writeOnlyPropCache;

Expand Down Expand Up @@ -684,7 +707,7 @@ let QUERYPARAM = 'query',
let { parametersResolution } = context.computedOptions;

// Override default value to schema for CONVERSION only for parmeter resolution set to schema
if (resolveFor === CONVERSION && parametersResolution === 'schema') {
if ((resolveFor === CONVERSION || resolveFor === TYPES_GENERATION) && parametersResolution === 'schema') {
if (!schema.hasOwnProperty('format')) {
schema.default = '<' + schema.type + '>';
}
Expand Down Expand Up @@ -743,16 +766,42 @@ let QUERYPARAM = 'query',
* @returns {Object} The processed schema details.
*/
processSchema = (resolvedSchema) => {
if (resolvedSchema.anyOf) {
return {
anyOf: resolvedSchema.anyOf.map((schema) => {
return processSchema(schema);
})
};
}

if (resolvedSchema.oneOf) {
return {
oneOf: resolvedSchema.oneOf.map((schema) => {
return processSchema(schema);
})
};
}

if (resolvedSchema.allOf) {
return {
allOf: resolvedSchema.allOf.map((schema) => {
return processSchema(schema);
})
};
}

if (resolvedSchema.type === 'object' && resolvedSchema.properties) {
const schemaDetails = {
description: resolvedSchema.description,
title: resolvedSchema.title,
type: resolvedSchema.type,
properties: {},
required: []
},
requiredProperties = new Set(resolvedSchema.required || []);

for (let [propName, propValue] of Object.entries(resolvedSchema.properties)) {
if (!propValue.type) {
if (!propValue.type && !propValue.anyOf && !propValue.oneOf && !propValue.allOf) {
continue;
}
const propertyDetails = {
Expand All @@ -772,7 +821,23 @@ let QUERYPARAM = 'query',
if (requiredProperties.has(propName)) {
schemaDetails.required.push(propName);
}
if (propValue.properties) {

if (propValue.anyOf) {
propertyDetails.anyOf = propValue.anyOf.map((schema) => {
return processSchema(schema);
});
}
else if (propValue.oneOf) {
propertyDetails.oneOf = propValue.oneOf.map((schema) => {
return processSchema(schema);
});
}
else if (propValue.allOf) {
propertyDetails.allOf = propValue.allOf.map((schema) => {
return processSchema(schema);
});
}
else if (propValue.properties) {
let processedProperties = processSchema(propValue);
propertyDetails.properties = processedProperties.properties;
if (processedProperties.required) {
Expand Down Expand Up @@ -1113,16 +1178,24 @@ let QUERYPARAM = 'query',
* Gets the description of the parameter.
* If the parameter is required, it prepends a `(Requried)` before the parameter description
* If the parameter type is enum, it appends the possible enum values
* @param {object} context - Global context object controlling behavior
* @param {object} parameter - input param for which description needs to be returned
* @returns {string} description of the parameters
*/
getParameterDescription = (parameter) => {
getParameterDescription = (context, parameter) => {
if (!_.isObject(parameter)) {
return '';
}

return (parameter.required ? '(Required) ' : '') + (parameter.description || '') +
(parameter.enum ? ' (This can only be one of ' + parameter.enum + ')' : '');
const requiredPrefix = (context && !context.enableTypeFetching && parameter.required ? '(Required) ' : ''),
desc = parameter.description || '';

let enumDescription = '';
if (parameter && parameter.schema && parameter.schema.enum && !(context && context.enableTypeFetching)) {
enumDescription = ' (This can only be one of ' + parameter.schema.enum + ')';
}

return requiredPrefix + desc + enumDescription;
},

/**
Expand All @@ -1141,7 +1214,7 @@ let QUERYPARAM = 'query',
{ enableOptionalParameters } = context.computedOptions;

let serialisedValue = '',
description = getParameterDescription(param),
description = getParameterDescription(context, param),
paramName = _.get(param, 'name'),
disabled = !enableOptionalParameters && _.get(param, 'required') !== true,
pmParams = [],
Expand Down Expand Up @@ -1421,7 +1494,6 @@ let QUERYPARAM = 'query',
});
});

// eslint-disable-next-line one-var
let responseExample,
responseExampleData;

Expand Down Expand Up @@ -1482,6 +1554,21 @@ let QUERYPARAM = 'query',
return [{ [bodyKey]: bodyData }];
}

// For type fetching, process the original schema before any modifications
// This is done to preserve the anyOf, oneOf etc in the original schema
// since they are otherwise flattened while resolving the schema
if (context.enableTypeFetching && requestBodySchema) {
const originalSchema = requestBodySchema.schema || requestBodySchema,
resolvedSchema = resolveSchema(
context,
originalSchema,
{ resolveFor: TYPES_GENERATION });

if (resolvedSchema.type || resolvedSchema.anyOf || resolvedSchema.oneOf || resolvedSchema.allOf) {
resolvedSchemaTypes.push(processSchema(resolvedSchema));
}
}

if (requestBodySchema.$ref) {
requestBodySchema = resolveSchema(
context,
Expand Down Expand Up @@ -1611,11 +1698,6 @@ let QUERYPARAM = 'query',

}

if (context.enableTypeFetching && requestBodySchema.type !== undefined) {
const requestBodySchemaTypes = processSchema(requestBodySchema);
resolvedSchemaTypes.push(requestBodySchemaTypes);
}

// Generate multiple examples when either request or response contains more than one example
if (
isExampleBody &&
Expand Down Expand Up @@ -1772,7 +1854,7 @@ let QUERYPARAM = 'query',
paramSchema.required = _.has(paramSchema, 'required') ?
paramSchema.required :
_.indexOf(requestBodySchema.required, key) !== -1;
description = getParameterDescription(paramSchema);
description = getParameterDescription(context, paramSchema);

if (typeof _.get(encoding, `[${key}].contentType`) === 'string') {
contentType = encoding[key].contentType;
Expand Down Expand Up @@ -2027,8 +2109,8 @@ let QUERYPARAM = 'query',
type: schema.type,
format: schema.format,
default: schema.default,
required: param.required || false,
deprecated: param.deprecated || false,
required: param.required,
deprecated: param.deprecated,
enum: schema.enum || undefined,
minLength: schema.minLength,
maxLength: schema.maxLength,
Expand All @@ -2054,14 +2136,18 @@ let QUERYPARAM = 'query',
param = resolveSchema(context, param);
}

if (_.has(param.schema, '$ref')) {
param.schema = resolveSchema(context, param.schema);
}

if (param.in !== QUERYPARAM || (!includeDeprecated && param.deprecated)) {
return;
}

const shouldResolveSchema = _.has(param, 'schema') &&
(_.has(param.schema, '$ref') || _.has(param.schema, 'anyOf') ||
_.has(param.schema, 'oneOf') || _.has(param.schema, 'allOf'));

if (shouldResolveSchema) {
param.schema = resolveSchema(context, param.schema);
}

let queryParamTypeInfo = {},
properties = {},
paramValue = resolveValueOfParameter(context, param);
Expand Down Expand Up @@ -2103,14 +2189,19 @@ let QUERYPARAM = 'query',
param = resolveSchema(context, param);
}

if (_.has(param.schema, '$ref')) {
param.schema = resolveSchema(context, param.schema);
}

if (param.in !== PATHPARAM) {
return;
}


const shouldResolveSchema = _.has(param, 'schema') &&
(_.has(param.schema, '$ref') || _.has(param.schema, 'anyOf') ||
_.has(param.schema, 'oneOf') || _.has(param.schema, 'allOf'));

if (shouldResolveSchema) {
param.schema = resolveSchema(context, param.schema);
}

let pathParamTypeInfo = {},
properties = {},
paramValue = resolveValueOfParameter(context, param);
Expand Down Expand Up @@ -2180,14 +2271,18 @@ let QUERYPARAM = 'query',
param = resolveSchema(context, param);
}

if (_.has(param.schema, '$ref')) {
param.schema = resolveSchema(context, param.schema);
}

if (param.in !== HEADER || (!includeDeprecated && param.deprecated)) {
return;
}

const shouldResolveSchema = _.has(param, 'schema') &&
(_.has(param.schema, '$ref') || _.has(param.schema, 'anyOf') ||
_.has(param.schema, 'oneOf') || _.has(param.schema, 'allOf'));

if (shouldResolveSchema) {
param.schema = resolveSchema(context, param.schema);
}

if (!keepImplicitHeaders && _.includes(IMPLICIT_HEADERS, _.toLower(_.get(param, 'name')))) {
return;
}
Expand Down Expand Up @@ -2340,14 +2435,25 @@ let QUERYPARAM = 'query',

headers.push(...serialisedHeader);

if (headerData && headerData.name && headerData.schema && headerData.schema.type) {
const { schema } = headerData;
if (headerData && headerData.name && headerData.schema) {
let { schema } = headerData;
const shouldResolveSchema = _.has(schema, '$ref') || _.has(schema, 'anyOf') ||
_.has(schema, 'oneOf') || _.has(schema, 'allOf');

if (shouldResolveSchema) {
schema = resolveSchema(context, schema);
}

if (!schema.type) {
return;
}

properties = {
type: schema.type,
format: schema.format,
default: schema.default,
required: schema.required || false,
deprecated: schema.deprecated || false,
required: schema.required,
deprecated: schema.deprecated,
enum: schema.enum || undefined,
minLength: schema.minLength,
maxLength: schema.maxLength,
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": "5.1.0",
"version": "5.2.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
4 changes: 3 additions & 1 deletion test/data/valid_openapi/test1.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,10 @@
"in": "query",
"description": "component level query param",
"required": false,
"deprecated": false,
"schema": {
"type": "string"
"type": "string",
"enum": ["medium"]
}
}
},
Expand Down
Loading
Loading