diff --git a/packages/bruno-app/src/components/ShareCollection/index.js b/packages/bruno-app/src/components/ShareCollection/index.js
index d3ffe083860..35419780729 100644
--- a/packages/bruno-app/src/components/ShareCollection/index.js
+++ b/packages/bruno-app/src/components/ShareCollection/index.js
@@ -14,7 +14,8 @@ import toast from 'react-hot-toast';
const EXPORT_FORMATS = {
ZIP: 'zip',
YAML: 'yaml',
- POSTMAN: 'postman'
+ POSTMAN_V2_0: 'postman-v2-0',
+ POSTMAN_V2_1: 'postman-v2-1'
};
const ShareCollection = ({ onClose, collectionUid }) => {
@@ -62,9 +63,9 @@ const ShareCollection = ({ onClose, collectionUid }) => {
exportOpenCollection(transformCollectionToSaveToExportAsFile(collectionCopy));
};
- const handleExportPostman = () => {
+ const handleExportPostman = (version) => {
const collectionCopy = cloneDeep(collection);
- exportPostmanCollection(collectionCopy);
+ exportPostmanCollection(collectionCopy, version);
};
const handleProceed = async () => {
@@ -79,8 +80,11 @@ const ShareCollection = ({ onClose, collectionUid }) => {
case EXPORT_FORMATS.YAML:
handleExportYaml();
break;
- case EXPORT_FORMATS.POSTMAN:
- handleExportPostman();
+ case EXPORT_FORMATS.POSTMAN_V2_0:
+ handleExportPostman('2.0');
+ break;
+ case EXPORT_FORMATS.POSTMAN_V2_1:
+ handleExportPostman('2.1');
break;
}
onClose();
@@ -165,20 +169,32 @@ const ShareCollection = ({ onClose, collectionUid }) => {
Other Format
!isDisabled && setSelectedFormat(EXPORT_FORMATS.POSTMAN)}
+ 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)}
+ >
+
+
+
+
+
Postman v2.0
+
Export for Postman Collection v2.0
+
+
+
!isDisabled && setSelectedFormat(EXPORT_FORMATS.POSTMAN_V2_1)}
>
-
Postman
-
Export for Postman
+
Postman v2.1
+
Export for Postman Collection v2.1
- {selectedFormat === EXPORT_FORMATS.POSTMAN && hasNonExportableRequestTypes.has && (
+ {(selectedFormat === EXPORT_FORMATS.POSTMAN_V2_0 || selectedFormat === EXPORT_FORMATS.POSTMAN_V2_1) && hasNonExportableRequestTypes.has && (
diff --git a/packages/bruno-app/src/utils/exporters/postman-collection.js b/packages/bruno-app/src/utils/exporters/postman-collection.js
index 87694c65286..645745a8880 100644
--- a/packages/bruno-app/src/utils/exporters/postman-collection.js
+++ b/packages/bruno-app/src/utils/exporters/postman-collection.js
@@ -2,11 +2,11 @@ import * as FileSaver from 'file-saver';
import { brunoToPostman } from '@usebruno/converters';
import { filterTransientItems } from 'utils/collections';
-export const exportCollection = (collection) => {
+export const exportCollection = (collection, version = '2.1') => {
// Filter out transient items before export
collection.items = filterTransientItems(collection.items);
- const collectionToExport = brunoToPostman(collection);
+ const collectionToExport = brunoToPostman(collection, version);
const fileName = `${collection.name}.json`;
const fileBlob = new Blob([JSON.stringify(collectionToExport, null, 2)], { type: 'application/json' });
diff --git a/packages/bruno-converters/src/postman/bruno-to-postman.js b/packages/bruno-converters/src/postman/bruno-to-postman.js
index c543efb46d0..bd73844efe5 100644
--- a/packages/bruno-converters/src/postman/bruno-to-postman.js
+++ b/packages/bruno-converters/src/postman/bruno-to-postman.js
@@ -147,18 +147,23 @@ export const sanitizeUrl = (url) => {
return sanitizedUrl;
};
-export const brunoToPostman = (collection) => {
+export const brunoToPostman = (collection, version = '2.1') => {
delete collection.uid;
delete collection.processEnvVariables;
deleteUidsInItems(collection.items);
deleteUidsInEnvs(collection.environments);
deleteSecretsInEnvs(collection.environments);
+ const schemaMap = {
+ '2.0': 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json',
+ '2.1': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
+ };
+
const generateInfoSection = () => {
return {
name: collection.name,
description: collection.root?.docs,
- schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
+ schema: schemaMap[version] || schemaMap['2.1']
};
};
@@ -377,44 +382,85 @@ export const brunoToPostman = (collection) => {
const generateAuth = (itemAuth) => {
switch (itemAuth?.mode) {
- case 'bearer':
+ case 'bearer': {
+ const token = itemAuth.bearer?.token || '';
+ if (version === '2.0') {
+ return {
+ type: 'bearer',
+ bearer: {
+ token: token
+ }
+ };
+ }
return {
type: 'bearer',
- bearer: {
- key: 'token',
- value: itemAuth.bearer?.token || '',
- type: 'string'
- }
+ bearer: [
+ {
+ key: 'token',
+ value: token,
+ type: 'string'
+ }
+ ]
};
+ }
case 'basic': {
+ const username = itemAuth.basic?.username || '';
+ const password = itemAuth.basic?.password || '';
+ if (version === '2.0') {
+ return {
+ type: 'basic',
+ basic: {
+ username: username,
+ password: password
+ }
+ };
+ }
return {
type: 'basic',
basic: [
{
- key: 'password',
- value: itemAuth.basic?.password || '',
+ key: 'username',
+ value: username,
type: 'string'
},
{
- key: 'username',
- value: itemAuth.basic?.username || '',
+ key: 'password',
+ value: password,
type: 'string'
}
]
};
}
case 'apikey': {
+ const key = itemAuth.apikey?.key || '';
+ const value = itemAuth.apikey?.value || '';
+ const inValue = itemAuth.apikey?.in || 'header';
+ if (version === '2.0') {
+ return {
+ type: 'apikey',
+ apikey: {
+ key: key,
+ value: value,
+ in: inValue
+ }
+ };
+ }
return {
type: 'apikey',
apikey: [
{
key: 'key',
- value: itemAuth.apikey?.key || '',
+ value: key,
type: 'string'
},
{
key: 'value',
- value: itemAuth.apikey?.value || '',
+ value: value,
+ type: 'string'
+ },
+ {
+ key: 'in',
+ value: inValue,
type: 'string'
}
]
diff --git a/packages/bruno-converters/tests/postman/bruno-to-postman.spec.js b/packages/bruno-converters/tests/postman/bruno-to-postman.spec.js
index c2b372d3801..344dd0a3f1c 100644
--- a/packages/bruno-converters/tests/postman/bruno-to-postman.spec.js
+++ b/packages/bruno-converters/tests/postman/bruno-to-postman.spec.js
@@ -412,11 +412,13 @@ describe('brunoToPostman null checks and fallbacks', () => {
const result = brunoToPostman(simpleCollection);
expect(result.item[0].request.auth).toEqual({
type: 'bearer',
- bearer: {
- key: 'token',
- value: '',
- type: 'string'
- }
+ bearer: [
+ {
+ key: 'token',
+ value: '',
+ type: 'string'
+ }
+ ]
});
});
@@ -443,12 +445,12 @@ describe('brunoToPostman null checks and fallbacks', () => {
type: 'basic',
basic: [
{
- key: 'password',
+ key: 'username',
value: '',
type: 'string'
},
{
- key: 'username',
+ key: 'password',
value: '',
type: 'string'
}
@@ -487,6 +489,11 @@ describe('brunoToPostman null checks and fallbacks', () => {
key: 'value',
value: '',
type: 'string'
+ },
+ {
+ key: 'in',
+ value: 'header',
+ type: 'string'
}
]
});
@@ -1047,3 +1054,178 @@ describe('brunoToPostman item ordering', () => {
expect(names).toEqual(['Apple', 'Mango', 'Zebra']);
});
});
+
+describe('brunoToPostman v2.0 format', () => {
+ it('should use v2.0 schema URL', () => {
+ const simpleCollection = {
+ name: 'Test Collection',
+ items: []
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.0');
+ expect(result.info.schema).toBe('https://schema.getpostman.com/json/collection/v2.0.0/collection.json');
+ });
+
+ it('should use object format for bearer auth in v2.0', () => {
+ const simpleCollection = {
+ items: [
+ {
+ name: 'Test Request',
+ type: 'http-request',
+ request: {
+ method: 'GET',
+ url: 'https://example.com',
+ auth: {
+ mode: 'bearer',
+ bearer: { token: 'test-token' }
+ }
+ }
+ }
+ ]
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.0');
+ expect(result.item[0].request.auth).toEqual({
+ type: 'bearer',
+ bearer: {
+ token: 'test-token'
+ }
+ });
+ });
+
+ it('should use object format for basic auth in v2.0', () => {
+ const simpleCollection = {
+ items: [
+ {
+ name: 'Test Request',
+ type: 'http-request',
+ request: {
+ method: 'GET',
+ url: 'https://example.com',
+ auth: {
+ mode: 'basic',
+ basic: { username: 'user', password: 'pass' }
+ }
+ }
+ }
+ ]
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.0');
+ expect(result.item[0].request.auth).toEqual({
+ type: 'basic',
+ basic: {
+ username: 'user',
+ password: 'pass'
+ }
+ });
+ });
+
+ it('should use object format for apikey auth in v2.0', () => {
+ const simpleCollection = {
+ items: [
+ {
+ name: 'Test Request',
+ type: 'http-request',
+ request: {
+ method: 'GET',
+ url: 'https://example.com',
+ auth: {
+ mode: 'apikey',
+ apikey: { key: 'api-key', value: 'secret', in: 'header' }
+ }
+ }
+ }
+ ]
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.0');
+ expect(result.item[0].request.auth).toEqual({
+ type: 'apikey',
+ apikey: {
+ key: 'api-key',
+ value: 'secret',
+ in: 'header'
+ }
+ });
+ });
+});
+
+describe('brunoToPostman v2.1 format', () => {
+ it('should use v2.1 schema URL by default', () => {
+ const simpleCollection = {
+ name: 'Test Collection',
+ items: []
+ };
+
+ const result = brunoToPostman(simpleCollection);
+ expect(result.info.schema).toBe('https://schema.getpostman.com/json/collection/v2.1.0/collection.json');
+ });
+
+ it('should use array format for bearer auth in v2.1', () => {
+ const simpleCollection = {
+ items: [
+ {
+ name: 'Test Request',
+ type: 'http-request',
+ request: {
+ method: 'GET',
+ url: 'https://example.com',
+ auth: {
+ mode: 'bearer',
+ bearer: { token: 'test-token' }
+ }
+ }
+ }
+ ]
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.1');
+ expect(result.item[0].request.auth).toEqual({
+ type: 'bearer',
+ bearer: [
+ {
+ key: 'token',
+ value: 'test-token',
+ type: 'string'
+ }
+ ]
+ });
+ });
+
+ it('should use array format for basic auth in v2.1', () => {
+ const simpleCollection = {
+ items: [
+ {
+ name: 'Test Request',
+ type: 'http-request',
+ request: {
+ method: 'GET',
+ url: 'https://example.com',
+ auth: {
+ mode: 'basic',
+ basic: { username: 'user', password: 'pass' }
+ }
+ }
+ }
+ ]
+ };
+
+ const result = brunoToPostman(simpleCollection, '2.1');
+ expect(result.item[0].request.auth).toEqual({
+ type: 'basic',
+ basic: [
+ {
+ key: 'username',
+ value: 'user',
+ type: 'string'
+ },
+ {
+ key: 'password',
+ value: 'pass',
+ type: 'string'
+ }
+ ]
+ });
+ });
+});