feat: add Postman v2.0 and v2.1 export support#7922
Conversation
- Add version parameter to brunoToPostman converter - Support v2.0 auth format (object-based) and v2.1 (array-based) - Update UI with separate export buttons for v2.0 and v2.1 - Add comprehensive tests for both Postman collection versions
WalkthroughThis PR adds support for exporting Bruno collections in Postman v2.0 or v2.1 formats. The ShareCollection UI now presents both versioned options instead of a single generic Postman export. The version parameter flows through the export pipeline and determines schema URLs and authentication structure generation in the bruno-to-postman converter. ChangesVersioned Postman Export
Sequence DiagramsequenceDiagram
participant User as User
participant UI as ShareCollection UI
participant Handler as Export Handler
participant Exporter as postman-collection
participant Converter as bruno-to-postman
User->>UI: Select Postman v2.0 or v2.1
UI->>Handler: handleExportPostman(version)
Handler->>Exporter: exportCollection(collection, version)
Exporter->>Converter: brunoToPostman(collection, version)
Note over Converter: Determine schema URL<br/>and auth structure<br/>by version
Converter-->>Exporter: Formatted Postman JSON
Exporter-->>Handler: Write JSON file
Handler-->>UI: Export complete
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly Related PRs
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (7)
packages/bruno-converters/src/postman/bruno-to-postman.js (2)
150-167: 💤 Low valueConsider validating
versionor exporting allowed versions as constants.Right now any unknown
version(e.g.'2.2','2.0.0', typo) silently falls back to the v2.1 schema URL on Line 166, butgenerateAuthonly special-cases the literal string'2.0'— so a typoed'2.0.0'would get a v2.1 schema URL paired with v2.1-shaped auth. Not a bug for current callers (they pass'2.0'/'2.1'only), but a small footgun. Either export shared constants and have callers use them, or validate the input and throw/warn for unsupported versions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/src/postman/bruno-to-postman.js` around lines 150 - 167, The function brunoToPostman accepts a version string but silently falls back to v2.1 for unknown values via schemaMap while generateAuth only special-cases '2.0', which can produce mismatched schema/auth; fix by defining and exporting allowed version constants (e.g., SUPPORTED_VERSIONS or POSTMAN_VERSIONS) and using them in brunoToPostman to validate the incoming version parameter, then either normalize or throw a clear error/warning for unsupported versions before using schemaMap; update references to schemaMap and generateAuth to rely on the same constants so callers and internals cannot pass/accept typoed versions like '2.0.0'.
383-475: 💤 Low valueAuth shape duplication is fine for now, but extractable.
The
if (version === '2.0') { ...object form... }then "else array form" pattern is repeated 3× for bearer/basic/apikey. Per the project's "avoid abstractions unless used in 3+ places" guideline, this is right at the threshold. A small helper liketoAuthValue(version, type, fields)that returns either the object or[{key, value, type:'string'}, ...]would tighten this up, but only if you find it adds clarity — keeping it explicit also reads well. Up to you.As per coding guidelines: "Avoid abstractions unless the exact same code is being used in more than 3 places."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/src/postman/bruno-to-postman.js` around lines 383 - 475, The generateAuth function duplicates the "object form when version === '2.0' else array form" logic for bearer/basic/apikey; extract a small helper (e.g., toAuthValue(version, type, fields)) and use it inside generateAuth to return either the object shape for v2.0 or the Postman key/value array shape for other versions, then replace the repeated blocks for 'bearer', 'basic', and 'apikey' to call toAuthValue with the appropriate field names (e.g., token; username/password; key/value/in) so the branching is centralized while keeping generateAuth's switch semantics intact.packages/bruno-app/src/components/ShareCollection/index.js (3)
197-197: 💤 Low valueMinor: condition will grow as more Postman versions land.
Each new Postman version forces another
||here. If you adopt the format-to-version map suggested above (or aisPostmanFormat(selectedFormat)predicate), this stays a single check. Not blocking.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-app/src/components/ShareCollection/index.js` at line 197, The condition checking multiple Postman versions (selectedFormat === EXPORT_FORMATS.POSTMAN_V2_0 || selectedFormat === EXPORT_FORMATS.POSTMAN_V2_1) will become unmanageable as versions grow; replace it with a single predicate or lookup (e.g., implement isPostmanFormat(selectedFormat) or a formatToVersion map) and use that in the JSX conditional alongside hasNonExportableRequestTypes.has so the check becomes isPostmanFormat(selectedFormat) && hasNonExportableRequestTypes.has; update or add the helper (isPostmanFormat or a formats set) near the EXPORT_FORMATS definition and reuse it in ShareCollection's render to simplify future additions.
171-194: 💤 Low valueAdd
data-testidto the new format cards for Playwright.The two new Postman cards are interactive selection elements and per project conventions for Playwright should be addressable via
data-testid. The existing ZIP/YAML cards are missing them too, but let's not add to the debt with the new ones.Suggested addition
<div + data-testid="export-format-postman-v2-0" className={`other-format-card ${selectedFormat === EXPORT_FORMATS.POSTMAN_V2_0 ? 'selected' : ''} ${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`} onClick={() => !isDisabled && setSelectedFormat(EXPORT_FORMATS.POSTMAN_V2_0)} > ... </div> <div + data-testid="export-format-postman-v2-1" className={`other-format-card ${selectedFormat === EXPORT_FORMATS.POSTMAN_V2_1 ? 'selected' : ''} ${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`} onClick={() => !isDisabled && setSelectedFormat(EXPORT_FORMATS.POSTMAN_V2_1)} >As per coding guidelines: "Add
data-testidto testable elements for Playwright."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-app/src/components/ShareCollection/index.js` around lines 171 - 194, Add data-testid attributes to the interactive Postman format cards in the ShareCollection component: for the div handling EXPORT_FORMATS.POSTMAN_V2_0 (the other-format-card with onClick that calls setSelectedFormat(EXPORT_FORMATS.POSTMAN_V2_0)) add a unique data-testid like "export-format-postman-v2-0"; likewise for the EXPORT_FORMATS.POSTMAN_V2_1 card (the other-format-card that sets EXPORT_FORMATS.POSTMAN_V2_1) add "export-format-postman-v2-1". Ensure the attributes are placed on the outer clickable divs (the ones using className "other-format-card" and onClick) so Playwright can target them.
83-88: 💤 Low valueOptional: derive version from the enum value rather than hardcoding strings.
Both
POSTMAN_V2_0→'2.0'andPOSTMAN_V2_1→'2.1'are duplicated knowledge between the enum and these case bodies. A tiny lookup keeps them in lock-step:Tiny lookup
+ const POSTMAN_VERSION_BY_FORMAT = { + [EXPORT_FORMATS.POSTMAN_V2_0]: '2.0', + [EXPORT_FORMATS.POSTMAN_V2_1]: '2.1' + }; ... - case EXPORT_FORMATS.POSTMAN_V2_0: - handleExportPostman('2.0'); - break; - case EXPORT_FORMATS.POSTMAN_V2_1: - handleExportPostman('2.1'); + case EXPORT_FORMATS.POSTMAN_V2_0: + case EXPORT_FORMATS.POSTMAN_V2_1: + handleExportPostman(POSTMAN_VERSION_BY_FORMAT[selectedFormat]); break;Totally optional given there are only two formats today.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-app/src/components/ShareCollection/index.js` around lines 83 - 88, The switch hardcodes Postman version strings for EXPORT_FORMATS.POSTMAN_V2_0 and POSTMAN_V2_1 when calling handleExportPostman; instead derive the version from the enum value (or from a tiny lookup keyed by EXPORT_FORMATS) so the mapping stays in sync — e.g., compute a version string from the enum key or create a small POSTMAN_VERSION_MAP and pass that into handleExportPostman rather than hardcoding '2.0' and '2.1'.packages/bruno-converters/tests/postman/bruno-to-postman.spec.js (2)
1058-1152: ⚡ Quick winCoverage gap: v2.0 auth with missing fields and default
in.The new v2.0 describe block only exercises happy paths with all fields present. The v2.0 branches in
generateAuthalso have fallback logic (itemAuth.bearer?.token || '',itemAuth.apikey?.in || 'header', etc.) that's only currently exercised against the v2.1 (default) shape. Worth adding at least:
- bearer with
token: nullunder'2.0'→bearer: { token: '' }- basic with missing username/password under
'2.0'→basic: { username: '', password: '' }- apikey under
'2.0'withoutin→apikey: { ..., in: 'header' }Keeps the v2.0 path symmetric with the existing null-fallback tests for the default version.
As per coding guidelines: "Cover both the 'happy path' and the realistically problematic paths."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/tests/postman/bruno-to-postman.spec.js` around lines 1058 - 1152, Add v2.0 tests that exercise the fallback branches in generateAuth by calling brunoToPostman with requests whose auth is missing or null: (1) bearer with bearer: { token: null } and assert result.item[0].request.auth.bearer.token === '' for version '2.0'; (2) basic with basic: {} (no username/password) and assert basic.username === '' and basic.password === ''; (3) apikey with apikey: { key: 'api-key', value: 'secret' } (omit in) and assert apikey.in === 'header' when using brunoToPostman(..., '2.0'). Use the same test structure and assertions style as the existing v2.0 cases to keep symmetry.
1154-1231: 💤 Low valueOptional: add an explicit v2.1 apikey test for symmetry.
The v2.0 block tests bearer/basic/apikey, but the v2.1 block only covers bearer and basic. The v2.1 apikey shape is already exercised indirectly via the "missing key/value in apikey auth" test (which hits the default version), but adding an explicit
'2.1'apikey case here keeps the two suites visually parallel and makes intent obvious to the next reader.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/bruno-converters/tests/postman/bruno-to-postman.spec.js` around lines 1154 - 1231, Add a new test in the "brunoToPostman v2.1 format" suite that calls brunoToPostman(simpleCollection, '2.1') with an http-request whose request.auth is mode: 'apikey' and apikey: { key: 'apiKey', value: 'abc' }, then assert result.item[0].request.auth equals the v2.1 array-shaped apikey object (type: 'apikey', apikey: [{ key: 'key', value: 'apiKey', type: 'string' }, { key: 'value', value: 'abc', type: 'string' }]) to mirror the existing v2.0 apikey test and keep parity with bearer/basic tests; reference brunoToPostman and result.item[0].request.auth to locate where to add the case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/bruno-app/src/components/ShareCollection/index.js`:
- Line 197: The condition checking multiple Postman versions (selectedFormat ===
EXPORT_FORMATS.POSTMAN_V2_0 || selectedFormat === EXPORT_FORMATS.POSTMAN_V2_1)
will become unmanageable as versions grow; replace it with a single predicate or
lookup (e.g., implement isPostmanFormat(selectedFormat) or a formatToVersion
map) and use that in the JSX conditional alongside
hasNonExportableRequestTypes.has so the check becomes
isPostmanFormat(selectedFormat) && hasNonExportableRequestTypes.has; update or
add the helper (isPostmanFormat or a formats set) near the EXPORT_FORMATS
definition and reuse it in ShareCollection's render to simplify future
additions.
- Around line 171-194: Add data-testid attributes to the interactive Postman
format cards in the ShareCollection component: for the div handling
EXPORT_FORMATS.POSTMAN_V2_0 (the other-format-card with onClick that calls
setSelectedFormat(EXPORT_FORMATS.POSTMAN_V2_0)) add a unique data-testid like
"export-format-postman-v2-0"; likewise for the EXPORT_FORMATS.POSTMAN_V2_1 card
(the other-format-card that sets EXPORT_FORMATS.POSTMAN_V2_1) add
"export-format-postman-v2-1". Ensure the attributes are placed on the outer
clickable divs (the ones using className "other-format-card" and onClick) so
Playwright can target them.
- Around line 83-88: The switch hardcodes Postman version strings for
EXPORT_FORMATS.POSTMAN_V2_0 and POSTMAN_V2_1 when calling handleExportPostman;
instead derive the version from the enum value (or from a tiny lookup keyed by
EXPORT_FORMATS) so the mapping stays in sync — e.g., compute a version string
from the enum key or create a small POSTMAN_VERSION_MAP and pass that into
handleExportPostman rather than hardcoding '2.0' and '2.1'.
In `@packages/bruno-converters/src/postman/bruno-to-postman.js`:
- Around line 150-167: The function brunoToPostman accepts a version string but
silently falls back to v2.1 for unknown values via schemaMap while generateAuth
only special-cases '2.0', which can produce mismatched schema/auth; fix by
defining and exporting allowed version constants (e.g., SUPPORTED_VERSIONS or
POSTMAN_VERSIONS) and using them in brunoToPostman to validate the incoming
version parameter, then either normalize or throw a clear error/warning for
unsupported versions before using schemaMap; update references to schemaMap and
generateAuth to rely on the same constants so callers and internals cannot
pass/accept typoed versions like '2.0.0'.
- Around line 383-475: The generateAuth function duplicates the "object form
when version === '2.0' else array form" logic for bearer/basic/apikey; extract a
small helper (e.g., toAuthValue(version, type, fields)) and use it inside
generateAuth to return either the object shape for v2.0 or the Postman key/value
array shape for other versions, then replace the repeated blocks for 'bearer',
'basic', and 'apikey' to call toAuthValue with the appropriate field names
(e.g., token; username/password; key/value/in) so the branching is centralized
while keeping generateAuth's switch semantics intact.
In `@packages/bruno-converters/tests/postman/bruno-to-postman.spec.js`:
- Around line 1058-1152: Add v2.0 tests that exercise the fallback branches in
generateAuth by calling brunoToPostman with requests whose auth is missing or
null: (1) bearer with bearer: { token: null } and assert
result.item[0].request.auth.bearer.token === '' for version '2.0'; (2) basic
with basic: {} (no username/password) and assert basic.username === '' and
basic.password === ''; (3) apikey with apikey: { key: 'api-key', value: 'secret'
} (omit in) and assert apikey.in === 'header' when using brunoToPostman(...,
'2.0'). Use the same test structure and assertions style as the existing v2.0
cases to keep symmetry.
- Around line 1154-1231: Add a new test in the "brunoToPostman v2.1 format"
suite that calls brunoToPostman(simpleCollection, '2.1') with an http-request
whose request.auth is mode: 'apikey' and apikey: { key: 'apiKey', value: 'abc'
}, then assert result.item[0].request.auth equals the v2.1 array-shaped apikey
object (type: 'apikey', apikey: [{ key: 'key', value: 'apiKey', type: 'string'
}, { key: 'value', value: 'abc', type: 'string' }]) to mirror the existing v2.0
apikey test and keep parity with bearer/basic tests; reference brunoToPostman
and result.item[0].request.auth to locate where to add the case.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ac004fb5-3cd9-4300-9abf-09441152b569
📒 Files selected for processing (4)
packages/bruno-app/src/components/ShareCollection/index.jspackages/bruno-app/src/utils/exporters/postman-collection.jspackages/bruno-converters/src/postman/bruno-to-postman.jspackages/bruno-converters/tests/postman/bruno-to-postman.spec.js
Description
Contribution Checklist:
Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.
Publishing to New Package Managers
Please see here for more information.
Summary by CodeRabbit