Skip to content

Commit 11b04ff

Browse files
Merge pull request #889 from postmanlabs/release/v5.3.3
Release version v5.3.3
2 parents d5f7e5d + 10b8ccc commit 11b04ff

File tree

5 files changed

+182
-7
lines changed

5 files changed

+182
-7
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.3.3] - 2025-10-14
6+
57
## [v5.3.2] - 2025-10-08
68

79
## [v5.3.1] - 2025-09-26
@@ -669,7 +671,9 @@ Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0
669671

670672
- Base release
671673

672-
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.3.2...HEAD
674+
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.3.3...HEAD
675+
676+
[v5.3.3]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.3.2...v5.3.3
673677

674678
[v5.3.2]: https://github.com/postmanlabs/openapi-to-postman/compare/v5.3.1...v5.3.2
675679

libV2/schemaUtils.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,10 +2145,17 @@ let QUERYPARAM = 'query',
21452145

21462146
createProperties = (param) => {
21472147
const { schema } = param;
2148+
2149+
// Handle union types (OpenAPI 3.1.x supports arrays of types like ["string", "integer"])
2150+
// Pick the first type if it's a union of types, but only if array is not empty
2151+
const resolvedType = Array.isArray(schema.type) ?
2152+
(schema.type.length > 0 ? schema.type[0] : undefined) :
2153+
schema.type;
2154+
21482155
return {
21492156
description: schema.description,
21502157
title: schema.title,
2151-
type: schema.type,
2158+
type: resolvedType,
21522159
format: schema.format,
21532160
default: schema.default,
21542161
required: param.required,
@@ -2490,8 +2497,14 @@ let QUERYPARAM = 'query',
24902497
return;
24912498
}
24922499

2500+
// Handle union types (OpenAPI 3.1.x supports arrays of types like ["string", "integer"])
2501+
// Pick the first type if it's a union of types, but only if array is not empty
2502+
const resolvedType = Array.isArray(schema.type) ?
2503+
(schema.type.length > 0 ? schema.type[0] : undefined) :
2504+
schema.type;
2505+
24932506
properties = {
2494-
type: schema.type,
2507+
type: resolvedType,
24952508
description: schema.description,
24962509
title: schema.title,
24972510
format: schema.format,
@@ -2506,6 +2519,7 @@ let QUERYPARAM = 'query',
25062519
pattern: schema.pattern,
25072520
example: schema.example
25082521
};
2522+
25092523
headerTypeInfo = { keyName: headerData.name, properties };
25102524
headerTypes.push(headerTypeInfo);
25112525
}

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.3.2",
3+
"version": "5.3.3",
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/unit/convertV2WithTypes.test.js

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,5 +1162,162 @@ describe('convertV2WithTypes', function() {
11621162
done();
11631163
});
11641164
});
1165-
});
11661165

1166+
it('should pick up the first type for handling union types in parameters and response headers', function(done) {
1167+
const openApiWithUnionTypes = {
1168+
openapi: '3.1.0',
1169+
info: {
1170+
title: 'Union Types Test API',
1171+
version: '1.0.0'
1172+
},
1173+
paths: {
1174+
'/users/{id}': {
1175+
get: {
1176+
parameters: [
1177+
{
1178+
name: 'id',
1179+
in: 'path',
1180+
required: true,
1181+
schema: {
1182+
type: ['string', 'integer'], // Union type - should pick 'string' (first)
1183+
description: 'User ID as string or integer'
1184+
}
1185+
},
1186+
{
1187+
name: 'format',
1188+
in: 'query',
1189+
schema: {
1190+
type: ['integer', 'string'], // Union type - should pick 'integer' (first)
1191+
description: 'Format preference'
1192+
}
1193+
},
1194+
{
1195+
name: 'auth',
1196+
in: 'header',
1197+
schema: {
1198+
type: ['string', 'null'], // Union type - should pick 'string' (first)
1199+
description: 'Authorization header'
1200+
}
1201+
},
1202+
{
1203+
name: 'singleTypeParam',
1204+
in: 'query',
1205+
schema: {
1206+
type: 'string', // Single type - should remain 'string'
1207+
description: 'Simple string parameter'
1208+
}
1209+
},
1210+
{
1211+
name: 'emptyUnionParam',
1212+
in: 'query',
1213+
schema: {
1214+
type: [], // Empty union type - should not have type property
1215+
description: 'Empty union type parameter'
1216+
}
1217+
}
1218+
],
1219+
responses: {
1220+
'200': {
1221+
description: 'Success response with union type headers',
1222+
headers: {
1223+
'x-rate-limit': {
1224+
description: 'Rate limit counter',
1225+
schema: {
1226+
type: ['integer', 'string'] // Union type - should pick 'integer' (first)
1227+
}
1228+
},
1229+
'x-request-id': {
1230+
description: 'Request identifier',
1231+
schema: {
1232+
type: ['string', 'number'] // Union type - should pick 'string' (first)
1233+
}
1234+
},
1235+
'x-single-type-header': {
1236+
description: 'Simple header',
1237+
schema: {
1238+
type: 'boolean' // Single type - should remain 'boolean'
1239+
}
1240+
}
1241+
},
1242+
content: {
1243+
'application/json': {
1244+
schema: {
1245+
type: 'object',
1246+
properties: {
1247+
id: { type: 'string' },
1248+
name: { type: 'string' }
1249+
}
1250+
}
1251+
}
1252+
}
1253+
}
1254+
}
1255+
}
1256+
}
1257+
}
1258+
};
1259+
1260+
Converter.convertV2WithTypes({ type: 'json', data: openApiWithUnionTypes }, {}, (err, conversionResult) => {
1261+
expect(err).to.be.null;
1262+
expect(conversionResult.extractedTypes).to.be.an('object').that.is.not.empty;
1263+
1264+
const extractedTypes = conversionResult.extractedTypes;
1265+
const requestTypes = extractedTypes['get/users/{id}'];
1266+
expect(requestTypes).to.be.an('object');
1267+
1268+
// Check path parameters
1269+
const pathParams = JSON.parse(requestTypes.request.pathParam);
1270+
expect(pathParams).to.be.an('array').with.length(1);
1271+
1272+
const idParam = pathParams.find((p) => { return p.keyName === 'id'; });
1273+
expect(idParam).to.be.an('object');
1274+
expect(idParam.properties.type).to.equal('string'); // First type from ['string', 'integer']
1275+
1276+
// Check query parameters
1277+
const queryParams = JSON.parse(requestTypes.request.queryParam);
1278+
expect(queryParams).to.be.an('array').with.length(3);
1279+
1280+
const formatParam = queryParams.find((p) => { return p.keyName === 'format'; });
1281+
expect(formatParam).to.be.an('object');
1282+
expect(formatParam.properties.type).to.equal('integer'); // First type from ['integer', 'string']
1283+
1284+
const singleTypeParam = queryParams.find((p) => { return p.keyName === 'singleTypeParam'; });
1285+
expect(singleTypeParam).to.be.an('object');
1286+
expect(singleTypeParam.properties.type).to.equal('string'); // Single type should remain unchanged
1287+
1288+
const emptyUnionParam = queryParams.find((p) => { return p.keyName === 'emptyUnionParam'; });
1289+
expect(emptyUnionParam).to.be.an('object');
1290+
expect(emptyUnionParam.properties).to.not.have.property('type'); // Empty union should not have type property
1291+
1292+
// Check request header parameters
1293+
const headerParams = JSON.parse(requestTypes.request.headers);
1294+
expect(headerParams).to.be.an('array').with.length(1);
1295+
1296+
const authParam = headerParams.find((p) => { return p.keyName === 'auth'; });
1297+
expect(authParam).to.be.an('object');
1298+
expect(authParam.properties.type).to.equal('string'); // First type from ['string', 'null']
1299+
1300+
// Check response headers - use the first available response status for tests
1301+
const responseStatuses = Object.keys(requestTypes.response);
1302+
expect(responseStatuses).to.be.an('array').that.is.not.empty;
1303+
1304+
const firstResponseStatus = responseStatuses[0];
1305+
const responseHeaders = JSON.parse(requestTypes.response[firstResponseStatus].headers);
1306+
expect(responseHeaders).to.be.an('array').with.length(3);
1307+
1308+
const rateLimitHeader = responseHeaders.find((h) => { return h.keyName === 'x-rate-limit'; });
1309+
expect(rateLimitHeader).to.be.an('object');
1310+
expect(rateLimitHeader.properties.type).to.equal('integer'); // First type from ['integer', 'string']
1311+
1312+
const requestIdHeader = responseHeaders.find((h) => { return h.keyName === 'x-request-id'; });
1313+
expect(requestIdHeader).to.be.an('object');
1314+
expect(requestIdHeader.properties.type).to.equal('string'); // First type from ['string', 'number']
1315+
1316+
const singleTypeHeader = responseHeaders.find((h) => { return h.keyName === 'x-single-type-header'; });
1317+
expect(singleTypeHeader).to.be.an('object');
1318+
expect(singleTypeHeader.properties.type).to.equal('boolean'); // Single type should remain unchanged
1319+
1320+
done();
1321+
});
1322+
});
1323+
});

0 commit comments

Comments
 (0)