fix: query variable referencing custom variable (#10023)

* fix: custom variable to query mapping

* chore: add test
This commit is contained in:
Ashwin Bhatkal
2026-01-19 19:22:58 +05:30
committed by GitHub
parent 31e9e896ec
commit 444161671d
4 changed files with 132 additions and 3 deletions

View File

@@ -94,7 +94,7 @@ function DashboardVariableSelection(): JSX.Element | null {
cycleNodes,
});
}
}, [setVariablesToGetUpdated, variables, variablesTableData]);
}, [variables, variablesTableData]);
// this handles the case where the dependency order changes i.e. variable list updated via creation or deletion etc. and we need to refetch the variables
// also trigger when the global time changes

View File

@@ -257,6 +257,15 @@ export const onUpdateVariableNode = (
): void => {
const visited = new Set<string>();
// If nodeToUpdate is not in topologicalOrder (e.g., CUSTOM variable),
// we still need to mark its children as needing updates
if (!topologicalOrder.includes(nodeToUpdate)) {
// Mark direct children of the node as visited so they get processed
(graph[nodeToUpdate] || []).forEach((child) => {
visited.add(child);
});
}
// Start processing from the node to update
topologicalOrder.forEach((node) => {
if (node === nodeToUpdate || visited.has(node)) {

View File

@@ -1,4 +1,4 @@
import { areArraysEqual } from './util';
import { areArraysEqual, onUpdateVariableNode, VariableGraph } from './util';
describe('areArraysEqual', () => {
it('should return true for equal arrays with same order', () => {
@@ -31,3 +31,121 @@ describe('areArraysEqual', () => {
expect(areArraysEqual(array1, array2)).toBe(true);
});
});
describe('onUpdateVariableNode', () => {
// Graph structure:
// deployment -> namespace -> service -> pod
// deployment has no parents, namespace depends on deployment, etc.
const graph: VariableGraph = {
deployment: ['namespace'],
namespace: ['service'],
service: ['pod'],
pod: [],
customVar: ['namespace'], // CUSTOM variable that affects namespace
};
const topologicalOrder = ['deployment', 'namespace', 'service', 'pod'];
it('should call callback for the node and all its descendants', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('deployment', graph, topologicalOrder, callback);
expect(visited).toEqual(['deployment', 'namespace', 'service', 'pod']);
});
it('should call callback starting from a middle node', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('namespace', graph, topologicalOrder, callback);
expect(visited).toEqual(['namespace', 'service', 'pod']);
});
it('should only call callback for the leaf node when updating leaf', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('pod', graph, topologicalOrder, callback);
expect(visited).toEqual(['pod']);
});
it('should handle CUSTOM variable not in topologicalOrder by updating its children', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
// customVar is not in topologicalOrder but has namespace as a child
onUpdateVariableNode('customVar', graph, topologicalOrder, callback);
// Should process namespace and its descendants (service, pod)
expect(visited).toEqual(['namespace', 'service', 'pod']);
});
it('should handle node not in graph gracefully', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('unknownNode', graph, topologicalOrder, callback);
// Should not call callback for any node since unknownNode has no children
expect(visited).toEqual([]);
});
it('should handle empty graph', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('deployment', {}, topologicalOrder, callback);
// deployment is in topologicalOrder, so callback is called for it
expect(visited).toEqual(['deployment']);
});
it('should handle empty topologicalOrder', () => {
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode('deployment', graph, [], callback);
expect(visited).toEqual([]);
});
it('should handle CUSTOM variable with multiple children', () => {
const graphWithMultipleChildren: VariableGraph = {
...graph,
customMulti: ['namespace', 'service'], // CUSTOM variable affecting multiple nodes
};
const visited: string[] = [];
const callback = (node: string): void => {
visited.push(node);
};
onUpdateVariableNode(
'customMulti',
graphWithMultipleChildren,
topologicalOrder,
callback,
);
// Should process namespace, service, and pod (descendants)
expect(visited).toEqual(['namespace', 'service', 'pod']);
});
});

View File

@@ -318,7 +318,9 @@ function GridCardGraph({
version={version}
threshold={threshold}
headerMenuList={menuList}
isFetchingResponse={queryResponse.isFetching}
isFetchingResponse={
queryResponse.isFetching || variablesToGetUpdated.length > 0
}
setRequestData={setRequestData}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}