mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-24 17:03:47 +01:00
Merge pull request #544 from kapami/feature/export-to-postman-collection
Feature/export to postman collection
This commit is contained in:
commit
671fcecb38
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import exportBrunoCollection from 'utils/collections/export';
|
||||
import exportPostmanCollection from 'utils/exporters/postman-collection';
|
||||
import { toastError } from 'utils/common/error';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import Modal from 'components/Modal';
|
||||
import { transformCollectionToSaveToExportAsFile } from 'utils/collections/index';
|
||||
|
||||
const ExportCollection = ({ onClose, collection }) => {
|
||||
const handleExportBrunoCollection = () => {
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
exportBrunoCollection(transformCollectionToSaveToExportAsFile(collectionCopy));
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleExportPostmanCollection = () => {
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
exportPostmanCollection(collectionCopy);
|
||||
// exportPostmanCollection(transformCollectionToSaveToExportAsFile(collectionCopy));
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal size="sm" title="Export Collection" hideFooter={true} handleConfirm={onClose} handleCancel={onClose}>
|
||||
<div>
|
||||
<div className="text-link hover:underline cursor-pointer" onClick={handleExportBrunoCollection}>
|
||||
Bruno Collection
|
||||
</div>
|
||||
<div className="text-link hover:underline cursor-pointer mt-2" onClick={handleExportPostmanCollection}>
|
||||
Postman Collection
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExportCollection;
|
@ -14,6 +14,7 @@ import NewRequest from 'components/Sidebar/NewRequest';
|
||||
import NewFolder from 'components/Sidebar/NewFolder';
|
||||
import CollectionItem from './CollectionItem';
|
||||
import RemoveCollection from './RemoveCollection';
|
||||
import ExportCollection from './ExportCollection';
|
||||
import CollectionProperties from './CollectionProperties';
|
||||
import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search';
|
||||
import { isItemAFolder, isItemARequest, transformCollectionToSaveToExportAsFile } from 'utils/collections';
|
||||
@ -26,6 +27,7 @@ const Collection = ({ collection, searchText }) => {
|
||||
const [showNewFolderModal, setShowNewFolderModal] = useState(false);
|
||||
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
||||
const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false);
|
||||
const [showExportCollectionModal, setShowExportCollectionModal] = useState(false);
|
||||
const [showRemoveCollectionModal, setShowRemoveCollectionModal] = useState(false);
|
||||
const [collectionPropertiesModal, setCollectionPropertiesModal] = useState(false);
|
||||
const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed);
|
||||
@ -129,6 +131,9 @@ const Collection = ({ collection, searchText }) => {
|
||||
{showRemoveCollectionModal && (
|
||||
<RemoveCollection collection={collection} onClose={() => setShowRemoveCollectionModal(false)} />
|
||||
)}
|
||||
{showExportCollectionModal && (
|
||||
<ExportCollection collection={collection} onClose={() => setShowExportCollectionModal(false)} />
|
||||
)}
|
||||
{collectionPropertiesModal && (
|
||||
<CollectionProperties collection={collection} onClose={() => setCollectionPropertiesModal(false)} />
|
||||
)}
|
||||
@ -186,7 +191,7 @@ const Collection = ({ collection, searchText }) => {
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
handleExportClick(true);
|
||||
setShowExportCollectionModal(true);
|
||||
}}
|
||||
>
|
||||
Export
|
||||
|
@ -2,7 +2,7 @@ import * as FileSaver from 'file-saver';
|
||||
import get from 'lodash/get';
|
||||
import each from 'lodash/each';
|
||||
|
||||
const deleteUidsInItems = (items) => {
|
||||
export const deleteUidsInItems = (items) => {
|
||||
each(items, (item) => {
|
||||
delete item.uid;
|
||||
|
||||
@ -26,7 +26,7 @@ const deleteUidsInItems = (items) => {
|
||||
* Some of the models in the app are not consistent with the Collection Json format
|
||||
* This function is used to transform the models to the Collection Json format
|
||||
*/
|
||||
const transformItem = (items = []) => {
|
||||
export const transformItem = (items = []) => {
|
||||
each(items, (item) => {
|
||||
if (['http-request', 'graphql-request'].includes(item.type)) {
|
||||
item.request.query = item.request.params;
|
||||
@ -47,14 +47,14 @@ const transformItem = (items = []) => {
|
||||
});
|
||||
};
|
||||
|
||||
const deleteUidsInEnvs = (envs) => {
|
||||
export const deleteUidsInEnvs = (envs) => {
|
||||
each(envs, (env) => {
|
||||
delete env.uid;
|
||||
each(env.variables, (variable) => delete variable.uid);
|
||||
});
|
||||
};
|
||||
|
||||
const deleteSecretsInEnvs = (envs) => {
|
||||
export const deleteSecretsInEnvs = (envs) => {
|
||||
each(envs, (env) => {
|
||||
each(env.variables, (variable) => {
|
||||
if (variable.secret) {
|
||||
@ -64,7 +64,7 @@ const deleteSecretsInEnvs = (envs) => {
|
||||
});
|
||||
};
|
||||
|
||||
const exportCollection = (collection) => {
|
||||
export const exportCollection = (collection) => {
|
||||
// delete uids
|
||||
delete collection.uid;
|
||||
deleteUidsInItems(collection.items);
|
||||
|
214
packages/bruno-app/src/utils/exporters/postman-collection.js
Normal file
214
packages/bruno-app/src/utils/exporters/postman-collection.js
Normal file
@ -0,0 +1,214 @@
|
||||
import map from 'lodash/map';
|
||||
import * as FileSaver from 'file-saver';
|
||||
import { deleteSecretsInEnvs, deleteUidsInEnvs, deleteUidsInItems } from 'utils/collections/export';
|
||||
|
||||
export const exportCollection = (collection) => {
|
||||
delete collection.uid;
|
||||
deleteUidsInItems(collection.items);
|
||||
deleteUidsInEnvs(collection.environments);
|
||||
deleteSecretsInEnvs(collection.environments);
|
||||
|
||||
const generateInfoSection = () => {
|
||||
return {
|
||||
name: collection.name,
|
||||
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
|
||||
};
|
||||
};
|
||||
|
||||
const generateCollectionVars = (collection) => {
|
||||
const pattern = /{{[^{}]+}}/g;
|
||||
let listOfVars = [];
|
||||
|
||||
const findOccurrences = (obj, results) => {
|
||||
if (typeof obj === 'object') {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach((item) => findOccurrences(item, results));
|
||||
} else {
|
||||
for (const key in obj) {
|
||||
findOccurrences(obj[key], results);
|
||||
}
|
||||
}
|
||||
} else if (typeof obj === 'string') {
|
||||
obj.replace(pattern, (match) => {
|
||||
results.push(match.replace(/{{|}}/g, ''));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
findOccurrences(collection, listOfVars);
|
||||
|
||||
const finalArrayOfVars = [...new Set(listOfVars)];
|
||||
|
||||
return finalArrayOfVars.map((variable) => ({
|
||||
key: variable,
|
||||
value: '',
|
||||
type: 'default'
|
||||
}));
|
||||
};
|
||||
|
||||
const generateEventSection = (item) => {
|
||||
const eventArray = [];
|
||||
if (item.request.tests.length) {
|
||||
eventArray.push({
|
||||
listen: 'test',
|
||||
script: {
|
||||
exec: item.request.tests.split('\n')
|
||||
// type: 'text/javascript'
|
||||
}
|
||||
});
|
||||
}
|
||||
if (item.request.script.req) {
|
||||
eventArray.push({
|
||||
listen: 'prerequest',
|
||||
script: {
|
||||
exec: item.request.script.req.split('\n')
|
||||
// type: 'text/javascript'
|
||||
}
|
||||
});
|
||||
}
|
||||
return eventArray;
|
||||
};
|
||||
|
||||
const generateHeaders = (headersArray) => {
|
||||
return map(headersArray, (item) => {
|
||||
return {
|
||||
key: item.name,
|
||||
value: item.value,
|
||||
disabled: !item.enabled,
|
||||
type: 'default'
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const generateBody = (body) => {
|
||||
switch (body.mode) {
|
||||
case 'formUrlEncoded':
|
||||
return {
|
||||
mode: 'urlencoded',
|
||||
urlencoded: map(body.formUrlEncoded, (bodyItem) => {
|
||||
return {
|
||||
key: bodyItem.name,
|
||||
value: bodyItem.value,
|
||||
disabled: !bodyItem.enabled,
|
||||
type: 'default'
|
||||
};
|
||||
})
|
||||
};
|
||||
case 'multipartForm':
|
||||
return {
|
||||
mode: 'formdata',
|
||||
formdata: map(body.multipartForm, (bodyItem) => {
|
||||
return {
|
||||
key: bodyItem.name,
|
||||
value: bodyItem.value,
|
||||
disabled: !bodyItem.enabled,
|
||||
type: 'default'
|
||||
};
|
||||
})
|
||||
};
|
||||
case 'json':
|
||||
return {
|
||||
mode: 'raw',
|
||||
raw: body.json,
|
||||
options: {
|
||||
raw: {
|
||||
language: 'json'
|
||||
}
|
||||
}
|
||||
};
|
||||
case 'xml':
|
||||
return {
|
||||
mode: 'raw',
|
||||
raw: body.xml,
|
||||
options: {
|
||||
raw: {
|
||||
language: 'xml'
|
||||
}
|
||||
}
|
||||
};
|
||||
case 'text':
|
||||
return {
|
||||
mode: 'raw',
|
||||
raw: body.text,
|
||||
options: {
|
||||
raw: {
|
||||
language: 'text'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const generateAuth = (itemAuth) => {
|
||||
switch (itemAuth) {
|
||||
case 'bearer':
|
||||
return {
|
||||
type: 'bearer',
|
||||
bearer: {
|
||||
key: 'token',
|
||||
value: itemAuth.bearer.token,
|
||||
type: 'string'
|
||||
}
|
||||
};
|
||||
case 'basic': {
|
||||
return {
|
||||
type: 'basic',
|
||||
basic: [
|
||||
{
|
||||
key: 'password',
|
||||
value: itemAuth.basic.password,
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
key: 'username',
|
||||
value: itemAuth.basic.username,
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const generateRequestSection = (itemRequest) => {
|
||||
const requestObject = {
|
||||
method: itemRequest.method,
|
||||
header: generateHeaders(itemRequest.headers),
|
||||
url: itemRequest.url,
|
||||
auth: generateAuth(itemRequest.auth)
|
||||
};
|
||||
|
||||
if (itemRequest.body.mode != 'none') {
|
||||
requestObject.body = generateBody(itemRequest.body);
|
||||
}
|
||||
return requestObject;
|
||||
};
|
||||
|
||||
const generateItemSection = (itemsArray) => {
|
||||
return map(itemsArray, (item) => {
|
||||
if (item.type === 'folder') {
|
||||
return {
|
||||
name: item.name,
|
||||
item: item.items.length ? generateItemSection(item.items) : []
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
name: item.name,
|
||||
event: generateEventSection(item),
|
||||
request: generateRequestSection(item.request)
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
const collectionToExport = {};
|
||||
collectionToExport.info = generateInfoSection();
|
||||
collectionToExport.item = generateItemSection(collection.items);
|
||||
collectionToExport.variable = generateCollectionVars(collection);
|
||||
|
||||
const fileName = `${collection.name}.json`;
|
||||
const fileBlob = new Blob([JSON.stringify(collection, null, 2)], { type: 'application/json' });
|
||||
|
||||
FileSaver.saveAs(fileBlob, fileName);
|
||||
};
|
||||
|
||||
export default exportCollection;
|
Loading…
Reference in New Issue
Block a user