diff --git a/frontend/src/components/App/Layout.tsx b/frontend/src/components/App/Layout.tsx index c2bc188d91..7d21938d19 100644 --- a/frontend/src/components/App/Layout.tsx +++ b/frontend/src/components/App/Layout.tsx @@ -15,7 +15,7 @@ import { setConfig } from '../../redux/configSlice'; import { ConfigState } from '../../redux/configSlice'; import { useTypedSelector } from '../../redux/reducers/reducers'; import store from '../../redux/stores/store'; -import { fetchStatelessClusterKubeConfigs, processClusterComparison } from '../../stateless/'; +import { fetchStatelessClusterKubeConfigs, isEqualClusterConfigs } from '../../stateless/'; import ActionsNotifier from '../common/ActionsNotifier'; import AlertNotification from '../common/AlertNotification'; import Sidebar, { NavigationTabs } from '../Sidebar'; @@ -56,6 +56,44 @@ function ClusterNotFoundPopup() { const Div = styled('div')``; const Main = styled('main')``; +/** + * Merges the new cluster with the current cluster. + * Stateless clusters are merged with the current cluster. + * It also preserves the useToken property. + * @param newConfig - The new cluster config. + * @param currentClusters - The current cluster config. + * @param statelessClusters - The stateless cluster config. + * @returns The merged cluster config. + */ +function mergeClusterConfigs( + newClusters: Record, + currentClusters: Record, + statelessClusters: Record | null +): Record { + const mergedClusters = { ...newClusters }; + + // Merge stateless clusters + if (statelessClusters) { + Object.entries(statelessClusters).forEach(([key, cluster]) => { + if (!mergedClusters[key]) { + mergedClusters[key] = cluster; + } + }); + } + + // Preserve useToken property + Object.entries(mergedClusters).forEach(([key, cluster]) => { + if (currentClusters[key]?.useToken !== undefined) { + mergedClusters[key] = { + ...cluster, + useToken: currentClusters[key].useToken, + }; + } + }); + + return mergedClusters; +} + export default function Layout({}: LayoutProps) { const arePluginsLoaded = useTypedSelector(state => state.plugins.loaded); const dispatch = useDispatch(); @@ -109,17 +147,22 @@ export default function Layout({}: LayoutProps) { if (clusters === null) { dispatch(setConfig(configToStore)); } else { - const isConfigDifferent = processClusterComparison(clusters, clustersToConfig, false); - - if ( - isConfigDifferent || - Object.keys(clustersToConfig).length !== Object.keys(clusters).length - ) { - if (statelessClusters !== null) { - processClusterComparison(clusters, statelessClusters, true); - } + // Check if the config is different + const configDifferent = isEqualClusterConfigs(clusters, clustersToConfig); - dispatch(setConfig(configToStore)); + if (configDifferent) { + // Merge the new config with the current config + const mergedClusters = mergeClusterConfigs( + configToStore.clusters, + clusters, + statelessClusters + ); + dispatch( + setConfig({ + ...configToStore, + clusters: mergedClusters, + }) + ); } } diff --git a/frontend/src/stateless/index.ts b/frontend/src/stateless/index.ts index dcae5569ec..065e3509c8 100644 --- a/frontend/src/stateless/index.ts +++ b/frontend/src/stateless/index.ts @@ -314,47 +314,31 @@ function generateSecureToken(length = 16): string { * Compares the cluster config from the backend and the redux store * @param clusters * @param clustersToConfig - * @param isStateless * @returns true if the present stored config is different from the fetched one. */ -export function processClusterComparison( - clusters: ConfigState['clusters'], - clustersToConfig: ConfigState['clusters'], - isStateless: boolean -) { - if (!clusters) { - // If no clusters, then add all clusters - return false; +export function isEqualClusterConfigs( + currentConfig: ConfigState['clusters'], + newConfig: ConfigState['clusters'] +): boolean { + if (!currentConfig || !newConfig) { + return true; // Config is different if either is null/undefined } - if (!clustersToConfig) { - // If no clustersToConfig, then delete all clusters - return true; - } - - let isConfigDifferent = false; - - Object.keys(clustersToConfig).forEach(key => { - if (!!clusters[key]) { - let clusterToCompare = clusters[key]; + const currentKeys = Object.keys(currentConfig); + const newKeys = Object.keys(newConfig); - if (clusterToCompare.useToken !== undefined) { - clusterToCompare = _.cloneDeep(clusters[key]); - delete clusterToCompare.useToken; - } + if (currentKeys.length !== newKeys.length) { + return true; // Different number of clusters + } - if (isStateless) { - if (_.isEqual(clustersToConfig[key], clusterToCompare)) { - delete clusters[key]; - } - } else { - isConfigDifferent = - isConfigDifferent || !_.isEqual(clustersToConfig[key], clusterToCompare); - } + return currentKeys.some(key => { + if (!newConfig[key]) { + return true; // Cluster in current config doesn't exist in new config } + const currentCluster = _.omit(currentConfig[key], ['useToken']); + const newCluster = _.omit(newConfig[key], ['useToken']); + return !_.isEqual(currentCluster, newCluster); }); - - return isConfigDifferent; } /** @@ -389,7 +373,6 @@ export async function fetchStatelessClusterKubeConfigs(dispatch: any) { }); const configToStore = { - ...config, statelessClusters: clustersToConfig, }; if (statelessClusters === null) { @@ -590,10 +573,12 @@ const exportFunctions = { getStatelessClusterKubeConfigs, findKubeconfigByClusterName, getUserIdFromLocalStorage, - processClusterComparison, + isEqualClusterConfigs, fetchStatelessClusterKubeConfigs, deleteClusterKubeconfig, updateStatelessClusterKubeconfig, + // @deprecated - use isEqualClusterConfigs instead + processClusterComparison: isEqualClusterConfigs, }; export default exportFunctions;