Skip to content

Commit b481d11

Browse files
authored
Merge pull request #960 from postmanlabs/release/v6.3.1
Release version v6.3.1
2 parents a3e689c + cf15b77 commit b481d11

11 files changed

Lines changed: 997 additions & 55 deletions

File tree

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+
## [v6.3.1] - 2026-07-23
6+
57
## [v6.3.0] - 2026-07-07
68

79
## [v6.2.0] - 2026-06-29
@@ -697,7 +699,9 @@ Newer releases follow the [Keep a Changelog](https://keepachangelog.com/en/1.0.0
697699

698700
- Base release
699701

700-
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v6.3.0...HEAD
702+
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v6.3.1...HEAD
703+
704+
[v6.3.1]: https://github.com/postmanlabs/openapi-to-postman/compare/v6.3.0...v6.3.1
701705

702706
[v6.3.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v6.2.0...v6.3.0
703707

libV2/CollectionGeneration/schemaUtils.js

Lines changed: 151 additions & 32 deletions
Large diffs are not rendered by default.

libV2/SpecificationCollectionSyncing/spec-to-collection/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ export function syncCollection(
106106
currentResponsesByCode[response.code].push(response);
107107
});
108108

109-
// The spec carries a single request example (the live body). It is applied to the originalRequest
110-
// of only the first response processed overall (isFirstSyncedResponse); every subsequent response
111-
// preserves its existing originalRequest body (see mergeResponseData), so a request change in the
112-
// spec updates just that first response rather than every response's originalRequest.
109+
// The spec carries a single live request (body + primary parameter values). It is applied to the
110+
// originalRequest of only the first response processed overall (isFirstSyncedResponse); every
111+
// subsequent response preserves its existing originalRequest request-side data — body, url
112+
// (query/path) and headers (see mergeResponseData) — so a request/parameter change in the spec
113+
// updates just that first response rather than every response's originalRequest.
113114
let isFirstSyncedResponse = true;
114115

115116
item.responses.each((response) => {

libV2/SpecificationCollectionSyncing/spec-to-collection/merge/ResponseMerger.ts

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Response, ResponseDefinition } from 'postman-collection';
1+
import { HeaderDefinition, Response, ResponseDefinition } from 'postman-collection';
22

33
import { mergeRequestAndResponseBodyRaw } from './BodyMerger';
44
import { mergeRequestAndResponseHeaders } from './HeaderMerger';
@@ -7,24 +7,78 @@ import { mergeRequestData } from './RequestMerger';
77
import { SyncOptions } from '../../shared';
88
import { attachImplicitHeaders } from '../header';
99

10+
/**
11+
* Preserve an existing saved response's header PARAMETER values while keeping any
12+
* implicit/generated headers (e.g. Content-Type, Accept) produced from the spec. Used when
13+
* preserving a non-first response's originalRequest on multi-example sync: for a shared key the
14+
* spec-generated header metadata (name casing, description, schema-derived flags) is kept and only
15+
* the existing header's `value`/`disabled` are copied over; generated target-only headers are
16+
* retained, and header params that only exist on the source are appended.
17+
*
18+
* Header keys are matched case-insensitively (HTTP header names are case-insensitive).
19+
* @param {HeaderDefinition[] | undefined} targetHeaders - Spec-generated headers (base)
20+
* @param {HeaderDefinition[] | undefined} sourceHeaders - Existing saved response headers to preserve
21+
* @returns {HeaderDefinition[] | undefined} Merged header list
22+
*/
23+
function preserveHeaderParams(
24+
targetHeaders: HeaderDefinition[] | undefined,
25+
sourceHeaders: HeaderDefinition[] | undefined
26+
): HeaderDefinition[] | undefined {
27+
if (!Array.isArray(sourceHeaders)) {
28+
return targetHeaders;
29+
}
30+
31+
const result: HeaderDefinition[] = Array.isArray(targetHeaders) ? [...targetHeaders] : [],
32+
indexByLowerKey = new Map<string, number>();
33+
34+
result.forEach((header, index) => {
35+
if (typeof header?.key === 'string') {
36+
indexByLowerKey.set(header.key.toLowerCase(), index);
37+
}
38+
});
39+
40+
sourceHeaders.forEach((sourceHeader) => {
41+
if (typeof sourceHeader?.key !== 'string') {
42+
return;
43+
}
44+
45+
const lowerKey = sourceHeader.key.toLowerCase();
46+
47+
if (indexByLowerKey.has(lowerKey)) {
48+
// Keep the spec-generated header (name casing + metadata); copy only the existing header's
49+
// value/disabled state onto it.
50+
const index = indexByLowerKey.get(lowerKey) as number;
51+
52+
result[index] = { ...result[index], value: sourceHeader.value, disabled: sourceHeader.disabled };
53+
}
54+
else {
55+
indexByLowerKey.set(lowerKey, result.length);
56+
result.push(sourceHeader);
57+
}
58+
});
59+
60+
return result;
61+
}
62+
1063
/**
1164
* Merges a single response from target with a corresponding response from current.
1265
* Returns the merged response definition.
1366
* @param {Response} targetResponse - Response from the target request
1467
* @param {Response } sourceResponse - Response from the source request
1568
* @param {SyncOptions} syncOptions - Options to control what should be synced
16-
* @param {boolean} preserveOriginalRequestBody - When true (and example syncing is enabled), keep
17-
* the existing response's `originalRequest.body` instead of overwriting it with the spec's request.
18-
* The spec carries a single request example (the live body) that maps to the first saved response;
19-
* set this for every response after the first so a request change in the spec doesn't overwrite
20-
* every response's originalRequest. Defaults to false (first response takes the spec request).
69+
* @param {boolean} preserveOriginalRequest - When true (and example syncing is enabled), keep the
70+
* existing response's `originalRequest` request-side data (body, url query/path variables and
71+
* headers) instead of overwriting it with the spec's request. The spec carries a single live
72+
* request (body + parameter values) that maps to the first saved response; set this for every
73+
* response after the first so a request/parameter change in the spec doesn't overwrite every
74+
* response's originalRequest. Defaults to false (first response takes the spec request).
2175
* @returns {ResponseDefinition} Merged response definition
2276
*/
2377
export function mergeResponseData(
2478
targetResponse: Response,
2579
sourceResponse: Response,
2680
syncOptions: SyncOptions,
27-
preserveOriginalRequestBody = false
81+
preserveOriginalRequest = false
2882
): ResponseDefinition {
2983
const targetRes: ResponseDefinition = targetResponse.toJSON(),
3084
sourceRes: ResponseDefinition = sourceResponse.toJSON(),
@@ -34,15 +88,32 @@ export function mergeResponseData(
3488
targetRes.originalRequest = mergeRequestData(targetRes.originalRequest, sourceRes.originalRequest, syncOptions);
3589

3690
/*
37-
* The spec carries a single request example (the live request body), which maps to the first
38-
* saved response. For the remaining responses, preserve their existing request body so that
39-
* editing the request in the spec doesn't overwrite every response's originalRequest on sync-back.
91+
* The spec carries a single live request (its body plus the primary parameter values), which
92+
* maps to the first saved response. For the remaining responses, preserve their existing
93+
* request-side data — body, url (query params + path variables) and headers — so that editing
94+
* the request or a parameter in the spec doesn't overwrite every response's originalRequest on
95+
* sync-back. Each component is deleted when the source had none, mirroring the body rule.
4096
* Only applies when example syncing is enabled (the multi-example flow).
4197
*/
42-
if (preserveOriginalRequestBody && shouldSyncExamples) {
43-
if (sourceRes.originalRequest.body === undefined) { delete targetRes.originalRequest.body; }
44-
else { targetRes.originalRequest.body = sourceRes.originalRequest.body; }
45-
}
98+
if (preserveOriginalRequest && shouldSyncExamples) {
99+
// Cast for the delete-when-source-had-none branches (url/body are not always optional in the type).
100+
const targetOriginalRequest = targetRes.originalRequest as unknown as Record<string, unknown>;
101+
102+
// Body: preserve the existing example's body wholesale (delete when the source had none).
103+
if (sourceRes.originalRequest.body === undefined) { delete targetOriginalRequest.body; }
104+
else { targetRes.originalRequest.body = sourceRes.originalRequest.body; }
105+
106+
// URL: preserve the existing example's url (query params + path variable values) wholesale.
107+
if (sourceRes.originalRequest.url === undefined) { delete targetOriginalRequest.url; }
108+
else { targetRes.originalRequest.url = sourceRes.originalRequest.url; }
109+
110+
// Headers: preserve the existing example's header PARAMETER values while keeping any
111+
// implicit/generated headers (e.g. Content-Type, Accept) the spec produced.
112+
targetRes.originalRequest.header = preserveHeaderParams(
113+
targetRes.originalRequest.header as HeaderDefinition[] | undefined,
114+
sourceRes.originalRequest.header as HeaderDefinition[] | undefined
115+
);
116+
}
46117
}
47118
// Attach implicit headers from the source response to the target response
48119
// if they are not present in the target response

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": "6.3.0",
3+
"version": "6.3.1",
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/CollectionGenerationAndSyncing/fixtures/OpenAPI3/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module.exports = {
3030
shouldSyncRequestToFirstResponseOnly: require('./shouldSyncRequestToFirstResponseOnly'),
3131
shouldPreserveOtherSameCodeResponsesOnRequestSync: require('./shouldPreserveOtherSameCodeResponsesOnRequestSync'),
3232
shouldPairSameCodeExamplesPositionallyOnSync: require('./shouldPairSameCodeExamplesPositionallyOnSync'),
33+
shouldPairParameterExamplesByMatchingKey: require('./shouldPairParameterExamplesByMatchingKey'),
3334
shouldDeleteOrphanRequestsWhenEnabled: require('./shouldDeleteOrphanRequestsWhenEnabled'),
3435
// Multi-file specification test cases
3536
multiFileSpecs: require('./multiFileSpecs')

0 commit comments

Comments
 (0)