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) {