From fceb99edc232370ef35e62c7a18c67375745675c Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <161328623+sanjaikumar-bruno@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:16:46 +0530 Subject: [PATCH] fix: autosave for environment tabs and folder-level auth (#6510) * feat: enhance autosave middleware to support environment draft and folder auth drafts * feat: extend autosave middleware to handle global and collection environment drafts * feat: update authentication components to use unified updateAuth function * refactor: rename updateAuth to updateFolderAuth for consistency in authentication components --- .../components/FolderSettings/Auth/index.js | 13 +- .../middlewares/autosave/middleware.js | 117 ++++++++++++++---- .../ReduxStore/slices/collections/index.js | 2 +- 3 files changed, 103 insertions(+), 29 deletions(-) diff --git a/packages/bruno-app/src/components/FolderSettings/Auth/index.js b/packages/bruno-app/src/components/FolderSettings/Auth/index.js index d91ba6089..a1995a205 100644 --- a/packages/bruno-app/src/components/FolderSettings/Auth/index.js +++ b/packages/bruno-app/src/components/FolderSettings/Auth/index.js @@ -3,7 +3,7 @@ import get from 'lodash/get'; import StyledWrapper from './StyledWrapper'; import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions'; import OAuth2AuthorizationCode from 'components/RequestPane/Auth/OAuth2/AuthorizationCode/index'; -import { updateFolderAuth } from 'providers/ReduxStore/slices/collections'; +import { updateFolderAuth as _updateFolderAuth } from 'providers/ReduxStore/slices/collections'; import { useDispatch } from 'react-redux'; import OAuth2PasswordCredentials from 'components/RequestPane/Auth/OAuth2/PasswordCredentials/index'; import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCredentials/index'; @@ -20,7 +20,7 @@ import AwsV4Auth from 'components/RequestPane/Auth/AwsV4Auth'; import { humanizeRequestAuthMode, getTreePathFromCollectionToItem } from 'utils/collections/index'; import Button from 'ui/Button'; -const GrantTypeComponentMap = ({ collection, folder }) => { +const GrantTypeComponentMap = ({ collection, folder, updateFolderAuth }) => { const dispatch = useDispatch(); const save = () => { @@ -90,6 +90,13 @@ const Auth = ({ collection, folder }) => { dispatch(saveFolderRoot(collection.uid, folder.uid)); }; + const updateFolderAuth = ({ itemUid, ...rest }) => { + return _updateFolderAuth({ + ...rest, + folderUid: folder.uid + }); + }; + const getAuthView = () => { switch (authMode) { case 'basic': { @@ -178,7 +185,7 @@ const Auth = ({ collection, folder }) => { collection={collection} item={folder} /> - + ); } diff --git a/packages/bruno-app/src/providers/ReduxStore/middlewares/autosave/middleware.js b/packages/bruno-app/src/providers/ReduxStore/middlewares/autosave/middleware.js index a91599100..1357c95b2 100644 --- a/packages/bruno-app/src/providers/ReduxStore/middlewares/autosave/middleware.js +++ b/packages/bruno-app/src/providers/ReduxStore/middlewares/autosave/middleware.js @@ -1,4 +1,5 @@ -import { saveRequest, saveCollectionSettings, saveFolderRoot } from '../../slices/collections/actions'; +import { saveRequest, saveCollectionSettings, saveFolderRoot, saveEnvironment } from '../../slices/collections/actions'; +import { saveGlobalEnvironment } from '../../slices/global-environments'; import { flattenItems, isItemARequest, isItemAFolder } from 'utils/collections'; const actionsToIntercept = [ @@ -85,7 +86,11 @@ const actionsToIntercept = [ 'collections/updateCollectionDocs', 'collections/updateCollectionClientCertificates', 'collections/updateCollectionProtobuf', - 'collections/updateCollectionProxy' + 'collections/updateCollectionProxy', + + // Environment draft actions + 'collections/setEnvironmentsDraft', + 'globalEnvironments/setGlobalEnvironmentDraft' ]; // Simple object to track pending save timers @@ -105,7 +110,8 @@ const scheduleAutoSave = (key, save, interval) => { // Helper to find and schedule saves for all existing drafts const saveExistingDrafts = (dispatch, getState, interval) => { - const collections = getState().collections.collections; + const state = getState(); + const collections = state.collections.collections; collections.forEach((collection) => { // Check collection-level draft @@ -114,6 +120,15 @@ const saveExistingDrafts = (dispatch, getState, interval) => { scheduleAutoSave(key, () => dispatch(saveCollectionSettings(collection.uid, null, true)), interval); } + // Check collection environment drafts + if (collection.environmentsDraft) { + const { environmentUid, variables } = collection.environmentsDraft; + if (environmentUid && variables) { + const key = `environment-${collection.uid}-${environmentUid}`; + scheduleAutoSave(key, () => dispatch(saveEnvironment(variables, environmentUid, collection.uid)), interval); + } + } + // Check all items (requests and folders) for drafts const allItems = flattenItems(collection.items); allItems.forEach((item) => { @@ -128,6 +143,77 @@ const saveExistingDrafts = (dispatch, getState, interval) => { } }); }); + + // Check global environment drafts + const globalEnvironmentDraft = state.globalEnvironments?.globalEnvironmentDraft; + if (globalEnvironmentDraft) { + const { environmentUid, variables } = globalEnvironmentDraft; + if (environmentUid && variables) { + const key = `global-environment-${environmentUid}`; + scheduleAutoSave(key, () => dispatch(saveGlobalEnvironment({ variables, environmentUid })), interval); + } + } +}; + +// Helper to determine entity type and create save handler +const determineSaveHandler = (actionType, payload, dispatch, getState) => { + const { itemUid, folderUid, collectionUid, environmentUid } = payload; + + // Handle environment drafts + if (actionType === 'collections/setEnvironmentsDraft') { + if (!environmentUid || !collectionUid) return null; + return { + key: `environment-${collectionUid}-${environmentUid}`, + save: () => { + const state = getState(); + const collection = state.collections.collections.find((c) => c.uid === collectionUid); + const draft = collection?.environmentsDraft; + if (draft?.environmentUid === environmentUid && draft?.variables) { + dispatch(saveEnvironment(draft.variables, environmentUid, collectionUid)); + } + } + }; + } + + if (actionType === 'globalEnvironments/setGlobalEnvironmentDraft') { + if (!environmentUid) return null; + return { + key: `global-environment-${environmentUid}`, + save: () => { + const state = getState(); + const draft = state.globalEnvironments?.globalEnvironmentDraft; + if (draft?.environmentUid === environmentUid && draft?.variables) { + dispatch(saveGlobalEnvironment({ variables: draft.variables, environmentUid })); + } + } + }; + } + + // Handle folder actions + if (folderUid) { + return { + key: `folder-${folderUid}`, + save: () => dispatch(saveFolderRoot(collectionUid, folderUid, true)) + }; + } + + // Handle request actions + if (itemUid) { + return { + key: `request-${itemUid}`, + save: () => dispatch(saveRequest(itemUid, collectionUid, true)) + }; + } + + // Handle collection-level changes + if (collectionUid) { + return { + key: `collection-${collectionUid}`, + save: () => dispatch(saveCollectionSettings(collectionUid, null, true)) + }; + } + + return null; }; export const autosaveMiddleware = ({ dispatch, getState }) => (next) => (action) => { @@ -155,28 +241,9 @@ export const autosaveMiddleware = ({ dispatch, getState }) => (next) => (action) // Only handle actions that create dirty state if (!actionsToIntercept.includes(action.type)) return result; - const { itemUid, folderUid, collectionUid } = action.payload; - const interval = autoSave.interval; - - // Determine what to save based on what IDs are present - let key, save; - - if (itemUid) { - // Request change - key = `request-${itemUid}`; - save = () => dispatch(saveRequest(itemUid, collectionUid, true)); - } else if (folderUid) { - // Folder change - key = `folder-${folderUid}`; - save = () => dispatch(saveFolderRoot(collectionUid, folderUid, true)); - } else if (collectionUid) { - // Collection change - key = `collection-${collectionUid}`; - save = () => dispatch(saveCollectionSettings(collectionUid, null, true)); - } - - if (key && save) { - scheduleAutoSave(key, save, interval); + const handler = determineSaveHandler(action.type, action.payload, dispatch, getState); + if (handler) { + scheduleAutoSave(handler.key, handler.save, autoSave.interval); } return result; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index 0997c83a3..3b2d60153 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -2338,7 +2338,7 @@ export const collectionsSlice = createSlice({ const collection = findCollectionByUid(state.collections, action.payload.collectionUid); if (!collection) return; - const folder = collection ? findItemInCollection(collection, action.payload.itemUid) : null; + const folder = collection ? findItemInCollection(collection, action.payload.folderUid) : null; if (!folder) return; if (folder) {