From c62f96c96e25fc6ad00007955f0a10551d1f66d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Dathueyt?= Date: Wed, 22 Nov 2023 20:57:11 +0100 Subject: [PATCH 1/5] New clone option for folders --- .../Collection/CollectionItem/index.js | 40 +++++++++---------- .../ReduxStore/slices/collections/actions.js | 13 +++++- packages/bruno-electron/src/ipc/collection.js | 37 ++++++++--------- .../bruno-electron/src/utils/filesystem.js | 22 +++++++++- 4 files changed, 69 insertions(+), 43 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js index a90e2bd29..6fd863429 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -306,28 +306,26 @@ const CollectionItem = ({ item, collection, searchText }) => { > Rename +
{ + dropdownTippyRef.current.hide(); + setCloneItemModalOpen(true); + }} + > + Clone +
{!isFolder && ( - <> -
{ - dropdownTippyRef.current.hide(); - setCloneItemModalOpen(true); - }} - > - Clone -
-
{ - dropdownTippyRef.current.hide(); - handleClick(null); - handleRun(); - }} - > - Run -
- +
{ + dropdownTippyRef.current.hide(); + handleClick(null); + handleRun(); + }} + > + Run +
)} {!isFolder && item.type === 'http-request' && (
(dispatch, getStat } if (isItemAFolder(item)) { - throw new Error('Cloning folders is not supported yet'); + const folderWithSameNameExists = find( + collection.items, + (i) => i.type === 'folder' && trim(i.name) === trim(newName) + ); + + if (folderWithSameNameExists) { + return reject(new Error('Duplicate folder names under same parent folder are not allowed')); + } + + const collectionPath = `${collection.pathname}${PATH_SEPARATOR}${newName}`; + ipcRenderer.invoke('renderer:clone-folder', newName, item, collectionPath).then(resolve).catch(reject); + return; } const parentItem = findParentItemInCollection(collectionCopy, itemUid); diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index be6afbeab..f3eb78457 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -12,7 +12,8 @@ const { browseDirectory, createDirectory, searchForBruFiles, - sanitizeDirectoryName + sanitizeDirectoryName, + parseCollectionItems } = require('../utils/filesystem'); const { stringifyJson } = require('../utils/common'); const { openCollectionDialog } = require('../app/collections'); @@ -335,25 +336,6 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`collection: ${collectionPath} already exists`); } - // Recursive function to parse the collection items and create files/folders - const parseCollectionItems = (items = [], currentPath) => { - items.forEach((item) => { - if (['http-request', 'graphql-request'].includes(item.type)) { - const content = jsonToBru(item); - const filePath = path.join(currentPath, `${item.name}.bru`); - fs.writeFileSync(filePath, content); - } - if (item.type === 'folder') { - const folderPath = path.join(currentPath, item.name); - fs.mkdirSync(folderPath); - - if (item.items && item.items.length) { - parseCollectionItems(item.items, folderPath); - } - } - }); - }; - const parseEnvironments = (environments = [], collectionPath) => { const envDirPath = path.join(collectionPath, 'environments'); if (!fs.existsSync(envDirPath)) { @@ -391,6 +373,21 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); + ipcMain.handle('renderer:clone-folder', async (event, itemFolder, collectionPath) => { + try { + if (fs.existsSync(collectionPath)) { + throw new Error(`folder: ${collectionPath} already exists`); + } + + await createDirectory(collectionPath); + + // create folder and files based on another folder + await parseCollectionItems(itemFolder.items, collectionPath); + } catch (error) { + return Promise.reject(error); + } + }); + ipcMain.handle('renderer:resequence-items', async (event, itemsToResequence) => { try { for (let item of itemsToResequence) { diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index 4f3ea980b..ab3a8d88d 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -134,6 +134,25 @@ const sanitizeDirectoryName = (name) => { return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-'); }; +// Recursive function to parse the folder and create files/folders +const parseCollectionItems = (items = [], currentPath) => { + items.forEach((item) => { + if (['http-request', 'graphql-request'].includes(item.type)) { + const content = jsonToBru(item); + const filePath = path.join(currentPath, `${item.name}.bru`); + fs.writeFileSync(filePath, content); + } + if (item.type === 'folder') { + const folderPath = path.join(currentPath, item.name); + fs.mkdirSync(folderPath); + + if (item.items && item.items.length) { + parseCollectionItems(item.items, folderPath); + } + } + }); +}; + module.exports = { isValidPathname, exists, @@ -150,5 +169,6 @@ module.exports = { chooseFileToSave, searchForFiles, searchForBruFiles, - sanitizeDirectoryName + sanitizeDirectoryName, + parseCollectionItems }; From 379697a02da86cfe1a90fc725e9e2ee774d8f08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Dathueyt?= Date: Wed, 22 Nov 2023 21:12:26 +0100 Subject: [PATCH 2/5] Remove missing argument & moving parseCollectionItems --- .../ReduxStore/slices/collections/actions.js | 2 +- packages/bruno-electron/src/ipc/collection.js | 38 +++++++++++++++++++ .../bruno-electron/src/utils/filesystem.js | 22 +---------- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index bebabbb3c..dd9ff9455 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -329,7 +329,7 @@ export const cloneItem = (newName, itemUid, collectionUid) => (dispatch, getStat } const collectionPath = `${collection.pathname}${PATH_SEPARATOR}${newName}`; - ipcRenderer.invoke('renderer:clone-folder', newName, item, collectionPath).then(resolve).catch(reject); + ipcRenderer.invoke('renderer:clone-folder', item, collectionPath).then(resolve).catch(reject); return; } diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index f3eb78457..529626387 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -336,6 +336,25 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`collection: ${collectionPath} already exists`); } + // Recursive function to parse the collection items and create files/folders + const parseCollectionItems = (items = [], currentPath) => { + items.forEach((item) => { + if (['http-request', 'graphql-request'].includes(item.type)) { + const content = jsonToBru(item); + const filePath = path.join(currentPath, `${item.name}.bru`); + fs.writeFileSync(filePath, content); + } + if (item.type === 'folder') { + const folderPath = path.join(currentPath, item.name); + fs.mkdirSync(folderPath); + + if (item.items && item.items.length) { + parseCollectionItems(item.items, folderPath); + } + } + }); + }; + const parseEnvironments = (environments = [], collectionPath) => { const envDirPath = path.join(collectionPath, 'environments'); if (!fs.existsSync(envDirPath)) { @@ -379,6 +398,25 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`folder: ${collectionPath} already exists`); } + // Recursive function to parse the folder and create files/folders + const parseCollectionItems = (items = [], currentPath) => { + items.forEach((item) => { + if (['http-request', 'graphql-request'].includes(item.type)) { + const content = jsonToBru(item); + const filePath = path.join(currentPath, `${item.name}.bru`); + fs.writeFileSync(filePath, content); + } + if (item.type === 'folder') { + const folderPath = path.join(currentPath, item.name); + fs.mkdirSync(folderPath); + + if (item.items && item.items.length) { + parseCollectionItems(item.items, folderPath); + } + } + }); + }; + await createDirectory(collectionPath); // create folder and files based on another folder diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index ab3a8d88d..4f3ea980b 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -134,25 +134,6 @@ const sanitizeDirectoryName = (name) => { return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-'); }; -// Recursive function to parse the folder and create files/folders -const parseCollectionItems = (items = [], currentPath) => { - items.forEach((item) => { - if (['http-request', 'graphql-request'].includes(item.type)) { - const content = jsonToBru(item); - const filePath = path.join(currentPath, `${item.name}.bru`); - fs.writeFileSync(filePath, content); - } - if (item.type === 'folder') { - const folderPath = path.join(currentPath, item.name); - fs.mkdirSync(folderPath); - - if (item.items && item.items.length) { - parseCollectionItems(item.items, folderPath); - } - } - }); -}; - module.exports = { isValidPathname, exists, @@ -169,6 +150,5 @@ module.exports = { chooseFileToSave, searchForFiles, searchForBruFiles, - sanitizeDirectoryName, - parseCollectionItems + sanitizeDirectoryName }; From c018bfc044508889f7a4fd2f8053106b124d9c4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Dathueyt?= Date: Wed, 22 Nov 2023 21:14:37 +0100 Subject: [PATCH 3/5] Removed unused import --- packages/bruno-electron/src/ipc/collection.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 529626387..fc1e1444e 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -12,8 +12,7 @@ const { browseDirectory, createDirectory, searchForBruFiles, - sanitizeDirectoryName, - parseCollectionItems + sanitizeDirectoryName } = require('../utils/filesystem'); const { stringifyJson } = require('../utils/common'); const { openCollectionDialog } = require('../app/collections'); From db1883536ec0145a57e28314b5f92ce4ee91c93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Dathueyt?= Date: Wed, 22 Nov 2023 21:40:28 +0100 Subject: [PATCH 4/5] Fixed an oversight when cloning from the parent --- .../src/providers/ReduxStore/slices/collections/actions.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index dd9ff9455..9aa96146b 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -319,8 +319,10 @@ export const cloneItem = (newName, itemUid, collectionUid) => (dispatch, getStat } if (isItemAFolder(item)) { + const parentFolder = findParentItemInCollection(collection, item.uid) || collection; + const folderWithSameNameExists = find( - collection.items, + parentFolder.items, (i) => i.type === 'folder' && trim(i.name) === trim(newName) ); @@ -328,7 +330,7 @@ export const cloneItem = (newName, itemUid, collectionUid) => (dispatch, getStat return reject(new Error('Duplicate folder names under same parent folder are not allowed')); } - const collectionPath = `${collection.pathname}${PATH_SEPARATOR}${newName}`; + const collectionPath = `${parentFolder.pathname}${PATH_SEPARATOR}${newName}`; ipcRenderer.invoke('renderer:clone-folder', item, collectionPath).then(resolve).catch(reject); return; } From aa1cef9e701b67a1c513895ccceac371043eae65 Mon Sep 17 00:00:00 2001 From: StonyTV Date: Wed, 22 Nov 2023 21:40:28 +0100 Subject: [PATCH 5/5] Fixed an oversight when cloning from the parent --- .../src/providers/ReduxStore/slices/collections/actions.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index dd9ff9455..9aa96146b 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -319,8 +319,10 @@ export const cloneItem = (newName, itemUid, collectionUid) => (dispatch, getStat } if (isItemAFolder(item)) { + const parentFolder = findParentItemInCollection(collection, item.uid) || collection; + const folderWithSameNameExists = find( - collection.items, + parentFolder.items, (i) => i.type === 'folder' && trim(i.name) === trim(newName) ); @@ -328,7 +330,7 @@ export const cloneItem = (newName, itemUid, collectionUid) => (dispatch, getStat return reject(new Error('Duplicate folder names under same parent folder are not allowed')); } - const collectionPath = `${collection.pathname}${PATH_SEPARATOR}${newName}`; + const collectionPath = `${parentFolder.pathname}${PATH_SEPARATOR}${newName}`; ipcRenderer.invoke('renderer:clone-folder', item, collectionPath).then(resolve).catch(reject); return; }