mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 17:18:59 +08:00
This reverts commit dad72dd2950919d1d54716507b8bbe0546de6f04.
This commit is contained in:
parent
5839b65f7a
commit
56b17bcfef
@ -1,19 +1,9 @@
|
|||||||
import { Row } from 'antd';
|
import { Row } from 'antd';
|
||||||
import { isEmpty } from 'lodash-es';
|
import { isNull } from 'lodash-es';
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
||||||
|
|
||||||
import {
|
|
||||||
buildDependencies,
|
|
||||||
buildDependencyGraph,
|
|
||||||
buildParentDependencyGraph,
|
|
||||||
IDependencyData,
|
|
||||||
onUpdateVariableNode,
|
|
||||||
} from './util';
|
|
||||||
import VariableItem from './VariableItem';
|
import VariableItem from './VariableItem';
|
||||||
|
|
||||||
function DashboardVariableSelection(): JSX.Element | null {
|
function DashboardVariableSelection(): JSX.Element | null {
|
||||||
@ -31,14 +21,6 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
|
|
||||||
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
||||||
|
|
||||||
const [dependencyData, setDependencyData] = useState<IDependencyData | null>(
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
|
||||||
(state) => state.globalTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (variables) {
|
if (variables) {
|
||||||
const tableRowData = [];
|
const tableRowData = [];
|
||||||
@ -61,37 +43,35 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
}
|
}
|
||||||
}, [variables]);
|
}, [variables]);
|
||||||
|
|
||||||
useEffect(() => {
|
const onVarChanged = (name: string): void => {
|
||||||
if (variablesTableData.length > 0) {
|
/**
|
||||||
const depGrp = buildDependencies(variablesTableData);
|
* this function takes care of adding the dependent variables to current update queue and removing
|
||||||
const { order, graph } = buildDependencyGraph(depGrp);
|
* the updated variable name from the queue
|
||||||
const parentDependencyGraph = buildParentDependencyGraph(graph);
|
*/
|
||||||
setDependencyData({
|
const dependentVariables = variablesTableData
|
||||||
order,
|
?.map((variable: any) => {
|
||||||
graph,
|
if (variable.type === 'QUERY') {
|
||||||
parentDependencyGraph,
|
const re = new RegExp(`\\{\\{\\s*?\\.${name}\\s*?\\}\\}`); // regex for `{{.var}}`
|
||||||
});
|
const queryValue = variable.queryValue || '';
|
||||||
|
const dependVarReMatch = queryValue.match(re);
|
||||||
|
if (dependVarReMatch !== null && dependVarReMatch.length > 0) {
|
||||||
|
return variable.name;
|
||||||
}
|
}
|
||||||
}, [setVariablesToGetUpdated, 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
|
|
||||||
useEffect(
|
|
||||||
() => {
|
|
||||||
if (!isEmpty(dependencyData?.order)) {
|
|
||||||
setVariablesToGetUpdated(dependencyData?.order || []);
|
|
||||||
}
|
}
|
||||||
},
|
return null;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
})
|
||||||
[JSON.stringify(dependencyData?.order), minTime, maxTime],
|
.filter((val: string | null) => !isNull(val));
|
||||||
);
|
setVariablesToGetUpdated((prev) => [
|
||||||
|
...prev.filter((v) => v !== name),
|
||||||
|
...dependentVariables,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
const onValueUpdate = (
|
const onValueUpdate = (
|
||||||
name: string,
|
name: string,
|
||||||
id: string,
|
id: string,
|
||||||
value: IDashboardVariable['selectedValue'],
|
value: IDashboardVariable['selectedValue'],
|
||||||
allSelected: boolean,
|
allSelected: boolean,
|
||||||
// isMountedCall?: boolean,
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
): void => {
|
): void => {
|
||||||
if (id) {
|
if (id) {
|
||||||
@ -131,20 +111,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dependencyData) {
|
onVarChanged(name);
|
||||||
const updatedVariables: string[] = [];
|
|
||||||
onUpdateVariableNode(
|
|
||||||
name,
|
|
||||||
dependencyData.graph,
|
|
||||||
dependencyData.order,
|
|
||||||
(node) => updatedVariables.push(node),
|
|
||||||
);
|
|
||||||
setVariablesToGetUpdated((prev) => [
|
|
||||||
...new Set([...prev, ...updatedVariables.filter((v) => v !== name)]),
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setVariablesToGetUpdated((prev) => prev.filter((v) => v !== name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,7 +139,6 @@ function DashboardVariableSelection(): JSX.Element | null {
|
|||||||
onValueUpdate={onValueUpdate}
|
onValueUpdate={onValueUpdate}
|
||||||
variablesToGetUpdated={variablesToGetUpdated}
|
variablesToGetUpdated={variablesToGetUpdated}
|
||||||
setVariablesToGetUpdated={setVariablesToGetUpdated}
|
setVariablesToGetUpdated={setVariablesToGetUpdated}
|
||||||
dependencyData={dependencyData}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -49,11 +49,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -70,11 +65,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -90,11 +80,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -124,11 +109,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -153,11 +133,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
@ -174,11 +149,6 @@ describe('VariableItem', () => {
|
|||||||
onValueUpdate={mockOnValueUpdate}
|
onValueUpdate={mockOnValueUpdate}
|
||||||
variablesToGetUpdated={[]}
|
variablesToGetUpdated={[]}
|
||||||
setVariablesToGetUpdated={(): void => {}}
|
setVariablesToGetUpdated={(): void => {}}
|
||||||
dependencyData={{
|
|
||||||
order: [],
|
|
||||||
graph: {},
|
|
||||||
parentDependencyGraph: {},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</MockQueryClientProvider>,
|
</MockQueryClientProvider>,
|
||||||
);
|
);
|
||||||
|
@ -35,10 +35,12 @@ import { popupContainer } from 'utils/selectPopupContainer';
|
|||||||
|
|
||||||
import { variablePropsToPayloadVariables } from '../utils';
|
import { variablePropsToPayloadVariables } from '../utils';
|
||||||
import { SelectItemStyle } from './styles';
|
import { SelectItemStyle } from './styles';
|
||||||
import { areArraysEqual, checkAPIInvocation, IDependencyData } from './util';
|
import { areArraysEqual } from './util';
|
||||||
|
|
||||||
const ALL_SELECT_VALUE = '__ALL__';
|
const ALL_SELECT_VALUE = '__ALL__';
|
||||||
|
|
||||||
|
const variableRegexPattern = /\{\{\s*?\.([^\s}]+)\s*?\}\}/g;
|
||||||
|
|
||||||
enum ToggleTagValue {
|
enum ToggleTagValue {
|
||||||
Only = 'Only',
|
Only = 'Only',
|
||||||
All = 'All',
|
All = 'All',
|
||||||
@ -55,7 +57,6 @@ interface VariableItemProps {
|
|||||||
) => void;
|
) => void;
|
||||||
variablesToGetUpdated: string[];
|
variablesToGetUpdated: string[];
|
||||||
setVariablesToGetUpdated: React.Dispatch<React.SetStateAction<string[]>>;
|
setVariablesToGetUpdated: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
dependencyData: IDependencyData | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSelectValue = (
|
const getSelectValue = (
|
||||||
@ -78,7 +79,6 @@ function VariableItem({
|
|||||||
onValueUpdate,
|
onValueUpdate,
|
||||||
variablesToGetUpdated,
|
variablesToGetUpdated,
|
||||||
setVariablesToGetUpdated,
|
setVariablesToGetUpdated,
|
||||||
dependencyData,
|
|
||||||
}: VariableItemProps): JSX.Element {
|
}: VariableItemProps): JSX.Element {
|
||||||
const [optionsData, setOptionsData] = useState<(string | number | boolean)[]>(
|
const [optionsData, setOptionsData] = useState<(string | number | boolean)[]>(
|
||||||
[],
|
[],
|
||||||
@ -88,20 +88,60 @@ function VariableItem({
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const validVariableUpdate = (): boolean => {
|
useEffect(() => {
|
||||||
if (!variableData.name) {
|
if (variableData.allSelected && variableData.type === 'QUERY') {
|
||||||
return false;
|
setVariablesToGetUpdated((prev) => {
|
||||||
|
const variablesQueue = [...prev.filter((v) => v !== variableData.name)];
|
||||||
|
if (variableData.name) {
|
||||||
|
variablesQueue.push(variableData.name);
|
||||||
}
|
}
|
||||||
|
return variablesQueue;
|
||||||
// variableData.name is present as the top element or next in the queue - variablesToGetUpdated
|
});
|
||||||
return Boolean(
|
}
|
||||||
variablesToGetUpdated.length &&
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
variablesToGetUpdated[0] === variableData.name,
|
}, [minTime, maxTime]);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState<null | string>(null);
|
const [errorMessage, setErrorMessage] = useState<null | string>(null);
|
||||||
|
|
||||||
|
const getDependentVariables = (queryValue: string): string[] => {
|
||||||
|
const matches = queryValue.match(variableRegexPattern);
|
||||||
|
|
||||||
|
// Extract variable names from the matches array without {{ . }}
|
||||||
|
return matches
|
||||||
|
? matches.map((match) => match.replace(variableRegexPattern, '$1'))
|
||||||
|
: [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getQueryKey = (variableData: IDashboardVariable): string[] => {
|
||||||
|
let dependentVariablesStr = '';
|
||||||
|
|
||||||
|
const dependentVariables = getDependentVariables(
|
||||||
|
variableData.queryValue || '',
|
||||||
|
);
|
||||||
|
|
||||||
|
const variableName = variableData.name || '';
|
||||||
|
|
||||||
|
dependentVariables?.forEach((element) => {
|
||||||
|
const [, variable] =
|
||||||
|
Object.entries(existingVariables).find(
|
||||||
|
([, value]) => value.name === element,
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
dependentVariablesStr += `${element}${variable?.selectedValue}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const variableKey = dependentVariablesStr.replace(/\s/g, '');
|
||||||
|
|
||||||
|
// added this time dependency for variables query as API respects the passed time range now
|
||||||
|
return [
|
||||||
|
REACT_QUERY_KEY.DASHBOARD_BY_ID,
|
||||||
|
variableName,
|
||||||
|
variableKey,
|
||||||
|
`${minTime}`,
|
||||||
|
`${maxTime}`,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
const getOptions = (variablesRes: VariableResponseProps | null): void => {
|
const getOptions = (variablesRes: VariableResponseProps | null): void => {
|
||||||
if (variablesRes && variableData.type === 'QUERY') {
|
if (variablesRes && variableData.type === 'QUERY') {
|
||||||
@ -144,7 +184,9 @@ function VariableItem({
|
|||||||
if (
|
if (
|
||||||
variableData.type === 'QUERY' &&
|
variableData.type === 'QUERY' &&
|
||||||
variableData.name &&
|
variableData.name &&
|
||||||
(validVariableUpdate() || valueNotInList || variableData.allSelected)
|
(variablesToGetUpdated.includes(variableData.name) ||
|
||||||
|
valueNotInList ||
|
||||||
|
variableData.allSelected)
|
||||||
) {
|
) {
|
||||||
let value = variableData.selectedValue;
|
let value = variableData.selectedValue;
|
||||||
let allSelected = false;
|
let allSelected = false;
|
||||||
@ -182,23 +224,8 @@ function VariableItem({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isLoading } = useQuery(
|
const { isLoading } = useQuery(getQueryKey(variableData), {
|
||||||
[
|
enabled: variableData && variableData.type === 'QUERY',
|
||||||
REACT_QUERY_KEY.DASHBOARD_BY_ID,
|
|
||||||
variableData.name || '',
|
|
||||||
`${minTime}`,
|
|
||||||
`${maxTime}`,
|
|
||||||
JSON.stringify(dependencyData?.order),
|
|
||||||
],
|
|
||||||
{
|
|
||||||
enabled:
|
|
||||||
variableData &&
|
|
||||||
variableData.type === 'QUERY' &&
|
|
||||||
checkAPIInvocation(
|
|
||||||
variablesToGetUpdated,
|
|
||||||
variableData,
|
|
||||||
dependencyData?.parentDependencyGraph,
|
|
||||||
),
|
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
dashboardVariablesQuery({
|
dashboardVariablesQuery({
|
||||||
query: variableData.queryValue || '',
|
query: variableData.queryValue || '',
|
||||||
@ -207,9 +234,6 @@ function VariableItem({
|
|||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
onSuccess: (response) => {
|
onSuccess: (response) => {
|
||||||
getOptions(response.payload);
|
getOptions(response.payload);
|
||||||
setVariablesToGetUpdated((prev) =>
|
|
||||||
prev.filter((v) => v !== variableData.name),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
onError: (error: {
|
onError: (error: {
|
||||||
details: {
|
details: {
|
||||||
@ -227,19 +251,9 @@ function VariableItem({
|
|||||||
setErrorMessage(message);
|
setErrorMessage(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const handleChange = (value: string | string[]): void => {
|
const handleChange = (value: string | string[]): void => {
|
||||||
// if value is equal to selected value then return
|
|
||||||
if (
|
|
||||||
value === variableData.selectedValue ||
|
|
||||||
(Array.isArray(value) &&
|
|
||||||
Array.isArray(variableData.selectedValue) &&
|
|
||||||
areArraysEqual(value, variableData.selectedValue))
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (variableData.name) {
|
if (variableData.name) {
|
||||||
if (
|
if (
|
||||||
value === ALL_SELECT_VALUE ||
|
value === ALL_SELECT_VALUE ||
|
||||||
|
@ -1,241 +0,0 @@
|
|||||||
import {
|
|
||||||
buildDependencies,
|
|
||||||
buildDependencyGraph,
|
|
||||||
buildParentDependencyGraph,
|
|
||||||
checkAPIInvocation,
|
|
||||||
onUpdateVariableNode,
|
|
||||||
VariableGraph,
|
|
||||||
} from '../util';
|
|
||||||
import {
|
|
||||||
buildDependenciesMock,
|
|
||||||
buildGraphMock,
|
|
||||||
checkAPIInvocationMock,
|
|
||||||
onUpdateVariableNodeMock,
|
|
||||||
} from './mock';
|
|
||||||
|
|
||||||
describe('dashboardVariables - utilities and processors', () => {
|
|
||||||
describe('onUpdateVariableNode', () => {
|
|
||||||
const { graph, topologicalOrder } = onUpdateVariableNodeMock;
|
|
||||||
const testCases = [
|
|
||||||
{
|
|
||||||
scenario: 'root element',
|
|
||||||
nodeToUpdate: 'deployment_environment',
|
|
||||||
expected: [
|
|
||||||
'deployment_environment',
|
|
||||||
'service_name',
|
|
||||||
'endpoint',
|
|
||||||
'http_status_code',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scenario: 'middle child',
|
|
||||||
nodeToUpdate: 'k8s_node_name',
|
|
||||||
expected: ['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scenario: 'leaf element',
|
|
||||||
nodeToUpdate: 'http_status_code',
|
|
||||||
expected: ['http_status_code'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scenario: 'node not in graph',
|
|
||||||
nodeToUpdate: 'unknown',
|
|
||||||
expected: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
scenario: 'node not in topological order',
|
|
||||||
nodeToUpdate: 'unknown',
|
|
||||||
expected: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
test.each(testCases)(
|
|
||||||
'should update variable node when $scenario',
|
|
||||||
({ nodeToUpdate, expected }) => {
|
|
||||||
const updatedVariables: string[] = [];
|
|
||||||
const callback = (node: string): void => {
|
|
||||||
updatedVariables.push(node);
|
|
||||||
};
|
|
||||||
|
|
||||||
onUpdateVariableNode(nodeToUpdate, graph, topologicalOrder, callback);
|
|
||||||
|
|
||||||
expect(updatedVariables).toEqual(expected);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should return empty array when topological order is empty', () => {
|
|
||||||
const updatedVariables: string[] = [];
|
|
||||||
onUpdateVariableNode('http_status_code', graph, [], (node) =>
|
|
||||||
updatedVariables.push(node),
|
|
||||||
);
|
|
||||||
expect(updatedVariables).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('checkAPIInvocation', () => {
|
|
||||||
const {
|
|
||||||
variablesToGetUpdated,
|
|
||||||
variableData,
|
|
||||||
parentDependencyGraph,
|
|
||||||
} = checkAPIInvocationMock;
|
|
||||||
|
|
||||||
const mockRootElement = {
|
|
||||||
name: 'deployment_environment',
|
|
||||||
key: '036a47cd-9ffc-47de-9f27-0329198964a8',
|
|
||||||
id: '036a47cd-9ffc-47de-9f27-0329198964a8',
|
|
||||||
modificationUUID: '5f71b591-f583-497c-839d-6a1590c3f60f',
|
|
||||||
selectedValue: 'production',
|
|
||||||
type: 'QUERY',
|
|
||||||
// ... other properties omitted for brevity
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
describe('edge cases', () => {
|
|
||||||
it('should return false when variableData is empty', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(
|
|
||||||
variablesToGetUpdated,
|
|
||||||
variableData,
|
|
||||||
parentDependencyGraph,
|
|
||||||
),
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true when parentDependencyGraph is empty', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(variablesToGetUpdated, variableData, {}),
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('variable sequences', () => {
|
|
||||||
it('should return true for valid sequence', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(
|
|
||||||
['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
variableData,
|
|
||||||
parentDependencyGraph,
|
|
||||||
),
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false for invalid sequence', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(
|
|
||||||
['k8s_cluster_name', 'k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
variableData,
|
|
||||||
parentDependencyGraph,
|
|
||||||
),
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return false when variableData is not in sequence', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(
|
|
||||||
['deployment_environment', 'service_name', 'endpoint'],
|
|
||||||
variableData,
|
|
||||||
parentDependencyGraph,
|
|
||||||
),
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('root element behavior', () => {
|
|
||||||
it('should return true for valid root element sequence', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation(
|
|
||||||
[
|
|
||||||
'deployment_environment',
|
|
||||||
'service_name',
|
|
||||||
'endpoint',
|
|
||||||
'http_status_code',
|
|
||||||
],
|
|
||||||
mockRootElement,
|
|
||||||
parentDependencyGraph,
|
|
||||||
),
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true for empty variablesToGetUpdated array', () => {
|
|
||||||
expect(
|
|
||||||
checkAPIInvocation([], mockRootElement, parentDependencyGraph),
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Graph Building Utilities', () => {
|
|
||||||
const { graph } = buildGraphMock;
|
|
||||||
const { variables } = buildDependenciesMock;
|
|
||||||
|
|
||||||
describe('buildParentDependencyGraph', () => {
|
|
||||||
it('should build parent dependency graph with correct relationships', () => {
|
|
||||||
const expected = {
|
|
||||||
deployment_environment: [],
|
|
||||||
service_name: ['deployment_environment'],
|
|
||||||
endpoint: ['deployment_environment', 'service_name'],
|
|
||||||
http_status_code: ['endpoint'],
|
|
||||||
k8s_cluster_name: [],
|
|
||||||
k8s_node_name: ['k8s_cluster_name'],
|
|
||||||
k8s_namespace_name: ['k8s_cluster_name', 'k8s_node_name'],
|
|
||||||
environment: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(buildParentDependencyGraph(graph)).toEqual(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty graph', () => {
|
|
||||||
expect(buildParentDependencyGraph({})).toEqual({});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('buildDependencyGraph', () => {
|
|
||||||
it('should build complete dependency graph with correct structure and order', () => {
|
|
||||||
const expected = {
|
|
||||||
graph: {
|
|
||||||
deployment_environment: ['service_name', 'endpoint'],
|
|
||||||
service_name: ['endpoint'],
|
|
||||||
endpoint: ['http_status_code'],
|
|
||||||
http_status_code: [],
|
|
||||||
k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
k8s_node_name: ['k8s_namespace_name'],
|
|
||||||
k8s_namespace_name: [],
|
|
||||||
environment: [],
|
|
||||||
},
|
|
||||||
order: [
|
|
||||||
'deployment_environment',
|
|
||||||
'k8s_cluster_name',
|
|
||||||
'environment',
|
|
||||||
'service_name',
|
|
||||||
'k8s_node_name',
|
|
||||||
'endpoint',
|
|
||||||
'k8s_namespace_name',
|
|
||||||
'http_status_code',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(buildDependencyGraph(graph)).toEqual(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('buildDependencies', () => {
|
|
||||||
it('should build dependency map from variables array', () => {
|
|
||||||
const expected: VariableGraph = {
|
|
||||||
deployment_environment: ['service_name', 'endpoint'],
|
|
||||||
service_name: ['endpoint'],
|
|
||||||
endpoint: ['http_status_code'],
|
|
||||||
http_status_code: [],
|
|
||||||
k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
k8s_node_name: ['k8s_namespace_name'],
|
|
||||||
k8s_namespace_name: [],
|
|
||||||
environment: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(buildDependencies(variables)).toEqual(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle empty variables array', () => {
|
|
||||||
expect(buildDependencies([])).toEqual({});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,251 +0,0 @@
|
|||||||
/* eslint-disable sonarjs/no-duplicate-string */
|
|
||||||
export const checkAPIInvocationMock = {
|
|
||||||
variablesToGetUpdated: [],
|
|
||||||
variableData: {
|
|
||||||
name: 'k8s_node_name',
|
|
||||||
key: '4d71d385-beaf-4434-8dbf-c62be68049fc',
|
|
||||||
allSelected: false,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '4d71d385-beaf-4434-8dbf-c62be68049fc',
|
|
||||||
modificationUUID: '77233d3c-96d7-4ccb-aa9d-11b04d563068',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 6,
|
|
||||||
queryValue:
|
|
||||||
"SELECT JSONExtractString(labels, 'k8s_node_name') AS k8s_node_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}}\nGROUP BY k8s_node_name",
|
|
||||||
selectedValue: 'gke-signoz-saas-si-consumer-bsc-e2sd4-a6d430fa-gvm2',
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
parentDependencyGraph: {
|
|
||||||
deployment_environment: [],
|
|
||||||
service_name: ['deployment_environment'],
|
|
||||||
endpoint: ['deployment_environment', 'service_name'],
|
|
||||||
http_status_code: ['endpoint'],
|
|
||||||
k8s_cluster_name: [],
|
|
||||||
environment: [],
|
|
||||||
k8s_node_name: ['k8s_cluster_name'],
|
|
||||||
k8s_namespace_name: ['k8s_cluster_name', 'k8s_node_name'],
|
|
||||||
},
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
export const onUpdateVariableNodeMock = {
|
|
||||||
nodeToUpdate: 'deployment_environment',
|
|
||||||
graph: {
|
|
||||||
deployment_environment: ['service_name', 'endpoint'],
|
|
||||||
service_name: ['endpoint'],
|
|
||||||
endpoint: ['http_status_code'],
|
|
||||||
http_status_code: [],
|
|
||||||
k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
environment: [],
|
|
||||||
k8s_node_name: ['k8s_namespace_name'],
|
|
||||||
k8s_namespace_name: [],
|
|
||||||
},
|
|
||||||
topologicalOrder: [
|
|
||||||
'deployment_environment',
|
|
||||||
'k8s_cluster_name',
|
|
||||||
'environment',
|
|
||||||
'service_name',
|
|
||||||
'k8s_node_name',
|
|
||||||
'endpoint',
|
|
||||||
'k8s_namespace_name',
|
|
||||||
'http_status_code',
|
|
||||||
],
|
|
||||||
callback: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildGraphMock = {
|
|
||||||
graph: {
|
|
||||||
deployment_environment: ['service_name', 'endpoint'],
|
|
||||||
service_name: ['endpoint'],
|
|
||||||
endpoint: ['http_status_code'],
|
|
||||||
http_status_code: [],
|
|
||||||
k8s_cluster_name: ['k8s_node_name', 'k8s_namespace_name'],
|
|
||||||
environment: [],
|
|
||||||
k8s_node_name: ['k8s_namespace_name'],
|
|
||||||
k8s_namespace_name: [],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildDependenciesMock = {
|
|
||||||
variables: [
|
|
||||||
{
|
|
||||||
key: '036a47cd-9ffc-47de-9f27-0329198964a8',
|
|
||||||
name: 'deployment_environment',
|
|
||||||
allSelected: false,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '036a47cd-9ffc-47de-9f27-0329198964a8',
|
|
||||||
modificationUUID: '5f71b591-f583-497c-839d-6a1590c3f60f',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 0,
|
|
||||||
queryValue:
|
|
||||||
"SELECT DISTINCT JSONExtractString(labels, 'deployment_environment') AS deployment_environment\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'signoz_calls_total'",
|
|
||||||
selectedValue: 'production',
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'eed5c917-1860-4c7e-bf6d-a05b97bafbc9',
|
|
||||||
name: 'service_name',
|
|
||||||
allSelected: true,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: 'eed5c917-1860-4c7e-bf6d-a05b97bafbc9',
|
|
||||||
modificationUUID: '85db928b-ac9b-4e9f-b274-791112102fdf',
|
|
||||||
multiSelect: true,
|
|
||||||
order: 1,
|
|
||||||
queryValue:
|
|
||||||
"SELECT DISTINCT JSONExtractString(labels, 'service_name') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' and JSONExtractString(labels, 'deployment_environment') = {{.deployment_environment}}",
|
|
||||||
selectedValue: ['otelgateway'],
|
|
||||||
showALLOption: true,
|
|
||||||
sort: 'ASC',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '4022d3c1-e845-4952-8984-78f25f575c7a',
|
|
||||||
name: 'endpoint',
|
|
||||||
allSelected: true,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '4022d3c1-e845-4952-8984-78f25f575c7a',
|
|
||||||
modificationUUID: 'c0107fa1-ebb7-4dd3-aa9d-6ba08ecc594d',
|
|
||||||
multiSelect: true,
|
|
||||||
order: 2,
|
|
||||||
queryValue:
|
|
||||||
"SELECT DISTINCT JSONExtractString(labels, 'operation') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' AND JSONExtractString(labels, 'service_name') IN {{.service_name}} and JSONExtractString(labels, 'deployment_environment') = {{.deployment_environment}}",
|
|
||||||
selectedValue: [
|
|
||||||
'//v1/traces',
|
|
||||||
'/logs/heroku',
|
|
||||||
'/logs/json',
|
|
||||||
'/logs/vector',
|
|
||||||
'/v1/logs',
|
|
||||||
'/v1/metrics',
|
|
||||||
'/v1/traces',
|
|
||||||
'SELECT',
|
|
||||||
'exporter/signozkafka/logs',
|
|
||||||
'exporter/signozkafka/metrics',
|
|
||||||
'exporter/signozkafka/traces',
|
|
||||||
'extension/signozkeyauth/Authenticate',
|
|
||||||
'get',
|
|
||||||
'hmget',
|
|
||||||
'opentelemetry.proto.collector.logs.v1.LogsService/Export',
|
|
||||||
'opentelemetry.proto.collector.metrics.v1.MetricsService/Export',
|
|
||||||
'opentelemetry.proto.collector.trace.v1.TraceService/Export',
|
|
||||||
'processor/signozlimiter/LogsProcessed',
|
|
||||||
'processor/signozlimiter/MetricsProcessed',
|
|
||||||
'processor/signozlimiter/TracesProcessed',
|
|
||||||
'receiver/otlp/LogsReceived',
|
|
||||||
'receiver/otlp/MetricsReceived',
|
|
||||||
'receiver/otlp/TraceDataReceived',
|
|
||||||
'receiver/signozhttplog/heroku/LogsReceived',
|
|
||||||
'receiver/signozhttplog/json/LogsReceived',
|
|
||||||
'receiver/signozhttplog/vector/LogsReceived',
|
|
||||||
'redis.dial',
|
|
||||||
'redis.pipeline eval',
|
|
||||||
'sadd',
|
|
||||||
'set',
|
|
||||||
'sismember',
|
|
||||||
],
|
|
||||||
showALLOption: true,
|
|
||||||
sort: 'ASC',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '5e8a3cd9-3cd9-42df-a76c-79471a0f75bd',
|
|
||||||
name: 'http_status_code',
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '5e8a3cd9-3cd9-42df-a76c-79471a0f75bd',
|
|
||||||
modificationUUID: '9a4021cc-a80a-4f15-8899-78892b763ca7',
|
|
||||||
multiSelect: true,
|
|
||||||
order: 3,
|
|
||||||
queryValue:
|
|
||||||
"SELECT DISTINCT JSONExtractString(labels, 'http_status_code') FROM signoz_metrics.distributed_time_series_v4_1day\n WHERE metric_name = 'signoz_calls_total' AND JSONExtractString(labels, 'operation') IN {{.endpoint}}",
|
|
||||||
showALLOption: true,
|
|
||||||
sort: 'ASC',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
selectedValue: ['', '200', '301', '400', '401', '405', '415', '429'],
|
|
||||||
allSelected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '48e9aa64-05ca-41c2-a1bd-6c8aeca659f1',
|
|
||||||
name: 'k8s_cluster_name',
|
|
||||||
allSelected: false,
|
|
||||||
customValue: 'test-1,\ntest-2,\ntest-3',
|
|
||||||
description: '',
|
|
||||||
id: '48e9aa64-05ca-41c2-a1bd-6c8aeca659f1',
|
|
||||||
modificationUUID: '44722322-368c-4613-bb7f-d0b12867d57a',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 4,
|
|
||||||
queryValue:
|
|
||||||
"SELECT JSONExtractString(labels, 'k8s_cluster_name') AS k8s_cluster_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time'\nGROUP BY k8s_cluster_name",
|
|
||||||
selectedValue: 'saasmonitor-cluster',
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3ea18ba2-30cf-4220-b03b-720b5eaf35f8',
|
|
||||||
name: 'environment',
|
|
||||||
allSelected: false,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '3ea18ba2-30cf-4220-b03b-720b5eaf35f8',
|
|
||||||
modificationUUID: '9f76cb06-1b9f-460f-a174-0b210bb3cf93',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 5,
|
|
||||||
queryValue:
|
|
||||||
"SELECT DISTINCT JSONExtractString(labels, 'deployment_environment') AS environment\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'signoz_calls_total'",
|
|
||||||
selectedValue: 'production',
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '4d71d385-beaf-4434-8dbf-c62be68049fc',
|
|
||||||
name: 'k8s_node_name',
|
|
||||||
allSelected: false,
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '4d71d385-beaf-4434-8dbf-c62be68049fc',
|
|
||||||
modificationUUID: '77233d3c-96d7-4ccb-aa9d-11b04d563068',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 6,
|
|
||||||
queryValue:
|
|
||||||
"SELECT JSONExtractString(labels, 'k8s_node_name') AS k8s_node_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_node_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}}\nGROUP BY k8s_node_name",
|
|
||||||
selectedValue: 'gke-signoz-saas-si-consumer-bsc-e2sd4-a6d430fa-gvm2',
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '937ecbae-b24b-4d6d-8cc4-5d5b8d53569b',
|
|
||||||
name: 'k8s_namespace_name',
|
|
||||||
customValue: '',
|
|
||||||
description: '',
|
|
||||||
id: '937ecbae-b24b-4d6d-8cc4-5d5b8d53569b',
|
|
||||||
modificationUUID: '8ad2442d-8b4d-4c64-848e-af847d1d0eec',
|
|
||||||
multiSelect: false,
|
|
||||||
order: 7,
|
|
||||||
queryValue:
|
|
||||||
"SELECT JSONExtractString(labels, 'k8s_namespace_name') AS k8s_namespace_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'k8s_pod_cpu_time' AND JSONExtractString(labels, 'k8s_cluster_name') = {{.k8s_cluster_name}} AND JSONExtractString(labels, 'k8s_node_name') IN {{.k8s_node_name}}\nGROUP BY k8s_namespace_name",
|
|
||||||
showALLOption: false,
|
|
||||||
sort: 'DISABLED',
|
|
||||||
textboxValue: '',
|
|
||||||
type: 'QUERY',
|
|
||||||
selectedValue: 'saasmonitor',
|
|
||||||
allSelected: false,
|
|
||||||
},
|
|
||||||
] as any,
|
|
||||||
};
|
|
@ -1,4 +1,3 @@
|
|||||||
import { isEmpty } from 'lodash-es';
|
|
||||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||||
|
|
||||||
export function areArraysEqual(
|
export function areArraysEqual(
|
||||||
@ -30,178 +29,3 @@ export const convertVariablesToDbFormat = (
|
|||||||
result[id] = obj;
|
result[id] = obj;
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const getDependentVariables = (queryValue: string): string[] => {
|
|
||||||
// Combined pattern for all formats:
|
|
||||||
// {{.variable_name}} - original format
|
|
||||||
// $variable_name - dollar prefix format
|
|
||||||
// [[variable_name]] - square bracket format
|
|
||||||
// {{variable_name}} - without dot format
|
|
||||||
const variableRegexPattern = /(?:\{\{\s*\.?([^\s}]+)\s*\}\}|\$([^\s\W]+)|\[\[([^\]]+)\]\])/g;
|
|
||||||
|
|
||||||
const matches = queryValue.match(variableRegexPattern);
|
|
||||||
|
|
||||||
// Extract variable names from the matches array, handling all formats
|
|
||||||
return matches
|
|
||||||
? matches.map((match) => {
|
|
||||||
if (match.startsWith('$')) {
|
|
||||||
return match.slice(1); // Remove $ prefix
|
|
||||||
}
|
|
||||||
if (match.startsWith('[[')) {
|
|
||||||
return match.slice(2, -2); // Remove [[ and ]]
|
|
||||||
}
|
|
||||||
// Handle both {{.var}} and {{var}} formats
|
|
||||||
return match.replace(/\{\{\s*\.?([^\s}]+)\s*\}\}/, '$1');
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
};
|
|
||||||
export type VariableGraph = Record<string, string[]>;
|
|
||||||
|
|
||||||
export const buildDependencies = (
|
|
||||||
variables: IDashboardVariable[],
|
|
||||||
): VariableGraph => {
|
|
||||||
const graph: VariableGraph = {};
|
|
||||||
|
|
||||||
// Initialize empty arrays for all variables first
|
|
||||||
variables.forEach((variable) => {
|
|
||||||
if (variable.name && variable.type === 'QUERY') {
|
|
||||||
graph[variable.name] = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// For each QUERY variable, add it as a dependent to its referenced variables
|
|
||||||
variables.forEach((variable) => {
|
|
||||||
if (variable.type === 'QUERY' && variable.name) {
|
|
||||||
const dependentVariables = getDependentVariables(variable.queryValue || '');
|
|
||||||
|
|
||||||
// For each referenced variable, add the current query as a dependent
|
|
||||||
dependentVariables.forEach((referencedVar) => {
|
|
||||||
if (graph[referencedVar]) {
|
|
||||||
graph[referencedVar].push(variable.name as string);
|
|
||||||
} else {
|
|
||||||
graph[referencedVar] = [variable.name as string];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return graph;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to build the dependency graph
|
|
||||||
export const buildDependencyGraph = (
|
|
||||||
dependencies: VariableGraph,
|
|
||||||
): { order: string[]; graph: VariableGraph } => {
|
|
||||||
const inDegree: Record<string, number> = {};
|
|
||||||
const adjList: VariableGraph = {};
|
|
||||||
|
|
||||||
// Initialize in-degree and adjacency list
|
|
||||||
Object.keys(dependencies).forEach((node) => {
|
|
||||||
if (!inDegree[node]) inDegree[node] = 0;
|
|
||||||
if (!adjList[node]) adjList[node] = [];
|
|
||||||
dependencies[node].forEach((child) => {
|
|
||||||
if (!inDegree[child]) inDegree[child] = 0;
|
|
||||||
inDegree[child]++;
|
|
||||||
adjList[node].push(child);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Topological sort using Kahn's Algorithm
|
|
||||||
const queue: string[] = Object.keys(inDegree).filter(
|
|
||||||
(node) => inDegree[node] === 0,
|
|
||||||
);
|
|
||||||
const topologicalOrder: string[] = [];
|
|
||||||
|
|
||||||
while (queue.length > 0) {
|
|
||||||
const current = queue.shift();
|
|
||||||
if (current === undefined) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
topologicalOrder.push(current);
|
|
||||||
|
|
||||||
adjList[current].forEach((neighbor) => {
|
|
||||||
inDegree[neighbor]--;
|
|
||||||
if (inDegree[neighbor] === 0) queue.push(neighbor);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topologicalOrder.length !== Object.keys(dependencies).length) {
|
|
||||||
console.error('Cycle detected in the dependency graph!');
|
|
||||||
}
|
|
||||||
|
|
||||||
return { order: topologicalOrder, graph: adjList };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onUpdateVariableNode = (
|
|
||||||
nodeToUpdate: string,
|
|
||||||
graph: VariableGraph,
|
|
||||||
topologicalOrder: string[],
|
|
||||||
callback: (node: string) => void,
|
|
||||||
): void => {
|
|
||||||
const visited = new Set<string>();
|
|
||||||
|
|
||||||
// Start processing from the node to update
|
|
||||||
topologicalOrder.forEach((node) => {
|
|
||||||
if (node === nodeToUpdate || visited.has(node)) {
|
|
||||||
visited.add(node);
|
|
||||||
callback(node);
|
|
||||||
(graph[node] || []).forEach((child) => {
|
|
||||||
visited.add(child);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const buildParentDependencyGraph = (
|
|
||||||
graph: VariableGraph,
|
|
||||||
): VariableGraph => {
|
|
||||||
const parentGraph: VariableGraph = {};
|
|
||||||
|
|
||||||
// Initialize empty arrays for all nodes
|
|
||||||
Object.keys(graph).forEach((node) => {
|
|
||||||
parentGraph[node] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
// For each node and its children in the original graph
|
|
||||||
Object.entries(graph).forEach(([node, children]) => {
|
|
||||||
// For each child, add the current node as its parent
|
|
||||||
children.forEach((child) => {
|
|
||||||
parentGraph[child].push(node);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return parentGraph;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkAPIInvocation = (
|
|
||||||
variablesToGetUpdated: string[],
|
|
||||||
variableData: IDashboardVariable,
|
|
||||||
parentDependencyGraph?: VariableGraph,
|
|
||||||
): boolean => {
|
|
||||||
if (isEmpty(variableData.name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmpty(parentDependencyGraph)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no dependency then true
|
|
||||||
const haveDependency =
|
|
||||||
parentDependencyGraph?.[variableData.name || '']?.length > 0;
|
|
||||||
if (!haveDependency) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if variable is in the list and has dependency then check if its the top element in the queue then true else false
|
|
||||||
return (
|
|
||||||
variablesToGetUpdated.length > 0 &&
|
|
||||||
variablesToGetUpdated[0] === variableData.name
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface IDependencyData {
|
|
||||||
order: string[];
|
|
||||||
graph: VariableGraph;
|
|
||||||
parentDependencyGraph: VariableGraph;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user