Skip to content

Commit bc0d024

Browse files
authored
Merge pull request #916 from postmanlabs/release/v5.6.0
Release version v5.6.0
2 parents 26e7394 + 55ba2bc commit bc0d024

File tree

11 files changed

+487
-25
lines changed

11 files changed

+487
-25
lines changed

CHANGELOG.md

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

33
## [Unreleased]
44

5+
## [v5.6.0] - 2025-11-28
6+
57
## [v5.5.0] - 2025-11-21
68

79
## [v5.4.1] - 2025-11-17
@@ -681,7 +683,9 @@ Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0
681683

682684
- Base release
683685

684-
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.5.0...HEAD
686+
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.6.0...HEAD
687+
688+
[v5.6.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.5.0...v5.6.0
685689

686690
[v5.5.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.4.1...v5.5.0
687691

libV2/helpers/collection/generateSkeletionTreeFromOpenAPI.js

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
let _ = require('lodash'),
22
Graph = require('graphlib').Graph,
3+
{ resolveRefFromSchema } = require('../../schemaUtils'),
34

45
PATH_WEBHOOK = 'path~webhook',
56
ALLOWED_HTTP_METHODS = {
@@ -14,8 +15,7 @@ let _ = require('lodash'),
1415
trace: true
1516
},
1617

17-
18-
_generateTreeFromPathsV2 = function (openapi, { includeDeprecated }) {
18+
_generateTreeFromPathsV2 = function (context, openapi, { includeDeprecated }) {
1919
/**
2020
* We will create a unidirectional graph
2121
*/
@@ -51,6 +51,10 @@ let _ = require('lodash'),
5151
if (pathSplit.length === 1) {
5252
let methods = openapi.paths[completePath];
5353

54+
if (methods && methods.$ref) {
55+
methods = resolveRefFromSchema(context, methods.$ref);
56+
}
57+
5458
_.forEach(methods, function (data, method) {
5559
if (!ALLOWED_HTTP_METHODS[method]) {
5660
return;
@@ -100,6 +104,10 @@ let _ = require('lodash'),
100104
if ((index + 1) === pathSplit.length) {
101105
let methods = openapi.paths[completePath];
102106

107+
if (methods && methods.$ref) {
108+
methods = resolveRefFromSchema(context, methods.$ref);
109+
}
110+
103111
_.forEach(methods, function (data, method) {
104112
if (!ALLOWED_HTTP_METHODS[method]) {
105113
return;
@@ -176,7 +184,7 @@ let _ = require('lodash'),
176184
return tree;
177185
},
178186

179-
_generateTreeFromTags = function (openapi, { includeDeprecated }) {
187+
_generateTreeFromTags = function (context, openapi, { includeDeprecated }) {
180188
let tree = new Graph(),
181189

182190
tagDescMap = _.reduce(openapi.tags, function (acc, data) {
@@ -216,6 +224,10 @@ let _ = require('lodash'),
216224
});
217225

218226
_.forEach(openapi.paths, function (methods, path) {
227+
if (methods && methods.$ref) {
228+
methods = resolveRefFromSchema(context, methods.$ref);
229+
}
230+
219231
_.forEach(methods, function (data, method) {
220232
if (!ALLOWED_HTTP_METHODS[method]) {
221233
return;
@@ -284,12 +296,13 @@ let _ = require('lodash'),
284296

285297
/**
286298
* Generates tree structure with nested folders based on tag order
299+
* @param {Object} context - Global context object
287300
* @param {Object} openapi - OpenAPI specification
288301
* @param {Object} options - Generation options
289302
* @param {boolean} options.includeDeprecated - Whether to include deprecated operations
290303
* @returns {Object} - Graph tree with nested folder structure
291304
*/
292-
_generateTreeFromNestedTags = function (openapi, { includeDeprecated }) {
305+
_generateTreeFromNestedTags = function (context, openapi, { includeDeprecated }) {
293306
let tree = new Graph(),
294307

295308
tagDescMap = _.reduce(openapi.tags, function (acc, data) {
@@ -345,6 +358,10 @@ let _ = require('lodash'),
345358
};
346359

347360
_.forEach(openapi.paths, function (methods, path) {
361+
if (methods && methods.$ref) {
362+
methods = resolveRefFromSchema(context, methods.$ref);
363+
}
364+
348365
_.forEach(methods, function (data, method) {
349366
if (!ALLOWED_HTTP_METHODS[method]) {
350367
return;
@@ -400,7 +417,7 @@ let _ = require('lodash'),
400417
return tree;
401418
},
402419

403-
_generateWebhookEndpoints = function (openapi, tree, { includeDeprecated }) {
420+
_generateWebhookEndpoints = function (context, openapi, tree, { includeDeprecated }) {
404421
if (!_.isEmpty(openapi.webhooks)) {
405422
tree.setNode(`${PATH_WEBHOOK}:folder`, {
406423
type: 'webhook~folder',
@@ -416,6 +433,10 @@ let _ = require('lodash'),
416433
}
417434

418435
_.forEach(openapi.webhooks, function (methodData, path) {
436+
if (methodData && methodData.$ref) {
437+
methodData = resolveRefFromSchema(context, methodData.$ref);
438+
}
439+
419440
_.forEach(methodData, function (data, method) {
420441
/**
421442
* include deprecated handling.
@@ -441,35 +462,37 @@ let _ = require('lodash'),
441462
/**
442463
* Used to generate a tree skeleton for the openapi which will be a collection
443464
*
465+
* @param {Object} context - Global context object
444466
* @param {Object} openapi - openapi schema paths in question
445467
* @param {String} stratergy='PATHS'
446468
*
447469
* @returns {Object} - tree format
448470
*/
449-
module.exports = function (openapi, { folderStrategy, includeWebhooks, includeDeprecated, nestedFolderHierarchy }) {
471+
module.exports = function (context, openapi,
472+
{ folderStrategy, includeWebhooks, includeDeprecated, nestedFolderHierarchy }) {
450473
let skeletonTree;
451474

452475
switch (folderStrategy) {
453476
case 'tags':
454477
if (nestedFolderHierarchy) {
455-
skeletonTree = _generateTreeFromNestedTags(openapi, { includeDeprecated });
478+
skeletonTree = _generateTreeFromNestedTags(context, openapi, { includeDeprecated });
456479
}
457480
else {
458-
skeletonTree = _generateTreeFromTags(openapi, { includeDeprecated });
481+
skeletonTree = _generateTreeFromTags(context, openapi, { includeDeprecated });
459482
}
460483

461484
break;
462485

463486
case 'paths':
464-
skeletonTree = _generateTreeFromPathsV2(openapi, { includeDeprecated });
487+
skeletonTree = _generateTreeFromPathsV2(context, openapi, { includeDeprecated });
465488
break;
466489

467490
default:
468491
throw new Error('generateSkeletonTreeFromOpenAPI~folderStrategy not valid');
469492
}
470493

471494
if (includeWebhooks) {
472-
skeletonTree = _generateWebhookEndpoints(openapi, skeletonTree, { includeDeprecated });
495+
skeletonTree = _generateWebhookEndpoints(context, openapi, skeletonTree, { includeDeprecated });
473496
}
474497

475498
return skeletonTree;

libV2/index.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const _ = require('lodash'),
1414
OpenApiErr = require('../lib/error'),
1515
{ validateTransaction, getMissingSchemaEndpoints } = require('./validationUtils');
1616

17-
const { resolvePostmanRequest } = require('./schemaUtils');
17+
const { resolvePostmanRequest, resolveRefFromSchema } = require('./schemaUtils');
1818
const { generateRequestItemObject, fixPathVariablesInUrl } = require('./utils');
1919

2020
module.exports = {
@@ -23,7 +23,7 @@ module.exports = {
2323
* Start generating the Bare bone tree that should exist for the schema
2424
*/
2525

26-
let collectionTree = generateSkeletonTreeFromOpenAPI(context.openapi, context.computedOptions);
26+
let collectionTree = generateSkeletonTreeFromOpenAPI(context, context.openapi, context.computedOptions);
2727

2828
/**
2929
* Do post order traversal so we get the request nodes first and generate the request object
@@ -92,11 +92,16 @@ module.exports = {
9292
let request = {},
9393
collectionVariables = [],
9494
requestObject = {},
95-
requestTypesObject = {};
95+
requestTypesObject = {},
96+
pathItem = context.openapi.paths[node.meta.path];
97+
98+
if (pathItem && pathItem.$ref) {
99+
pathItem = resolveRefFromSchema(context, pathItem.$ref);
100+
}
96101

97102
try {
98103
({ request, collectionVariables, requestTypesObject } = resolvePostmanRequest(context,
99-
context.openapi.paths[node.meta.path],
104+
pathItem,
100105
node.meta.path,
101106
node.meta.method
102107
));
@@ -166,7 +171,12 @@ module.exports = {
166171
// generate the request form the node
167172
let request = {},
168173
collectionVariables = [],
169-
requestObject = {};
174+
requestObject = {},
175+
webhookPathItem = context.openapi.webhooks[node.meta.path];
176+
177+
if (webhookPathItem && webhookPathItem.$ref) {
178+
webhookPathItem = resolveRefFromSchema(context, webhookPathItem.$ref);
179+
}
170180

171181
// TODO: Figure out a proper fix for this
172182
if (node.meta.method === 'parameters') {
@@ -175,7 +185,7 @@ module.exports = {
175185

176186
try {
177187
({ request, collectionVariables } = resolvePostmanRequest(context,
178-
context.openapi.webhooks[node.meta.path],
188+
webhookPathItem,
179189
node.meta.path,
180190
node.meta.method
181191
));

libV2/schemaUtils.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,8 @@ let QUERYPARAM = 'query',
303303
const { specComponents } = context,
304304
{ stackLimit } = context.computedOptions;
305305

306+
context.schemaCache = context.schemaCache || {};
307+
306308
if (stackDepth >= getRefStackLimit(stackLimit)) {
307309
return { value: ERR_TOO_MANY_LEVELS };
308310
}
@@ -721,7 +723,7 @@ let QUERYPARAM = 'query',
721723

722724
schema.properties = resolvedSchemaProps;
723725
}
724-
726+
725727
schema.type = schema.type || SCHEMA_TYPES.object;
726728
}
727729
// If schema is of type array
@@ -833,10 +835,10 @@ let QUERYPARAM = 'query',
833835

834836
if (resolvedSchema.type === 'object') {
835837
const schemaDetails = {
836-
description: resolvedSchema.description,
837-
title: resolvedSchema.title,
838-
type: resolvedSchema.type
839-
};
838+
description: resolvedSchema.description,
839+
title: resolvedSchema.title,
840+
type: resolvedSchema.type
841+
};
840842

841843
// Only include properties if they exist in the original schema
842844
if (resolvedSchema.hasOwnProperty('properties')) {

libV2/validationUtils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,6 +2721,10 @@ module.exports = {
27212721
);
27222722

27232723
_.forEach(schemaPaths, (schemaPathObj, schemaPath) => {
2724+
if (schemaPathObj && schemaPathObj.$ref) {
2725+
schemaPathObj = resolveRefFromSchema(context, schemaPathObj.$ref);
2726+
}
2727+
27242728
_.forEach(_.keys(schemaPathObj), (pathKey) => {
27252729
schemaJsonPath = `$.paths[${schemaPath}].${_.toLower(pathKey)}`;
27262730
let operationItem = _.get(schemaPathObj, pathKey) || {},

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": "5.5.0",
3+
"version": "5.6.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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
openapi: "3.1.0"
2+
info:
3+
version: 1.0.0
4+
title: Nested Path Item References Test
5+
description: API with nested path item references to test recursive resolution
6+
7+
paths:
8+
/users:
9+
$ref: "#/components/pathItems/UsersPathRef" # Level 1 ref -> Level 2 ref -> actual path item
10+
11+
/products:
12+
$ref: "#/components/pathItems/ProductsPath" # Direct ref to actual path item
13+
14+
/orders:
15+
get:
16+
summary: Get orders
17+
operationId: getOrders
18+
responses:
19+
'200':
20+
description: OK
21+
22+
components:
23+
pathItems:
24+
# This is a nested reference - points to another ref
25+
UsersPathRef:
26+
$ref: "#/components/pathItems/UsersPathLevel2"
27+
28+
# This is the second level - points to the actual path item
29+
UsersPathLevel2:
30+
$ref: "#/components/pathItems/UsersPathActual"
31+
32+
# This is the actual path item with operations
33+
UsersPathActual:
34+
get:
35+
summary: List users
36+
operationId: listUsers
37+
responses:
38+
'200':
39+
description: A list of users
40+
post:
41+
summary: Create user
42+
operationId: createUser
43+
responses:
44+
'201':
45+
description: User created
46+
delete:
47+
summary: Delete user
48+
operationId: deleteUser
49+
responses:
50+
'204':
51+
description: User deleted
52+
53+
# Direct path item (no nesting)
54+
ProductsPath:
55+
get:
56+
summary: Get products
57+
operationId: getProducts
58+
responses:
59+
'200':
60+
description: A list of products
61+
post:
62+
summary: Create product
63+
operationId: createProduct
64+
responses:
65+
'201':
66+
description: Product created
67+

0 commit comments

Comments
 (0)