Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 26 additions & 10 deletions packages/bruno-app/src/components/ShareCollection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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();
Expand Down Expand Up @@ -165,20 +169,32 @@ const ShareCollection = ({ onClose, collectionUid }) => {
<div className="section-title">Other Format</div>
<div className="other-format-grid">
<div
className={`other-format-card ${selectedFormat === EXPORT_FORMATS.POSTMAN ? 'selected' : ''} ${isDisabled ? 'opacity-50 cursor-not-allowed' : ''}`}
onClick={() => !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)}
>
<div className="format-icon">
<IconFileExport size={28} strokeWidth={1.5} />
</div>
<div className="format-info">
<div className="format-name">Postman v2.0</div>
<div className="format-description">Export for Postman Collection v2.0</div>
</div>
</div>
<div
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)}
>
<div className="format-icon">
<IconFileExport size={28} strokeWidth={1.5} />
</div>
<div className="format-info">
<div className="format-name">Postman</div>
<div className="format-description">Export for Postman</div>
<div className="format-name">Postman v2.1</div>
<div className="format-description">Export for Postman Collection v2.1</div>
</div>
</div>
</div>

{selectedFormat === EXPORT_FORMATS.POSTMAN && hasNonExportableRequestTypes.has && (
{(selectedFormat === EXPORT_FORMATS.POSTMAN_V2_0 || selectedFormat === EXPORT_FORMATS.POSTMAN_V2_1) && hasNonExportableRequestTypes.has && (
<div className="flex items-center mt-4 p-3 rounded" style={{ backgroundColor: 'rgba(251, 191, 36, 0.1)' }}>
<IconAlertTriangle size={16} className="mr-2 flex-shrink-0" style={{ color: '#f59e0b' }} />
<span className="text-sm" style={{ color: '#f59e0b' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
Expand Down
74 changes: 60 additions & 14 deletions packages/bruno-converters/src/postman/bruno-to-postman.js
Original file line number Diff line number Diff line change
Expand Up @@ -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']
};
};

Expand Down Expand Up @@ -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'
}
]
Expand Down
Loading