Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add jest test cases for new logic's utils, functions and processors - dashboardVariables #6621

Merged
merged 9 commits into from
Dec 17, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IDashboardVariable } from 'types/api/dashboard/getAll';
import {
buildDependencies,
buildDependencyGraph,
buildParentDependencyGraph,
onUpdateVariableNode,
VariableGraph,
} from './util';
Expand All @@ -29,6 +30,7 @@ function DashboardVariableSelection(): JSX.Element | null {
const [dependencyData, setDependencyData] = useState<{
order: string[];
graph: VariableGraph;
parentDependencyGraph: VariableGraph;
} | null>(null);

useEffect(() => {
Expand Down Expand Up @@ -59,9 +61,11 @@ function DashboardVariableSelection(): JSX.Element | null {
if (variablesTableData.length > 0 && !initializationRef.current) {
const depGrp = buildDependencies(variablesTableData);
const { order, graph } = buildDependencyGraph(depGrp);
const parentDependencyGraph = buildParentDependencyGraph(graph);
setDependencyData({
order,
graph,
parentDependencyGraph,
});
initializationRef.current = true;
}
Expand Down Expand Up @@ -120,7 +124,7 @@ function DashboardVariableSelection(): JSX.Element | null {
dependencyData.order,
(node) => updatedVariables.push(node),
);
setVariablesToGetUpdated(updatedVariables.filter((v) => v !== name)); // question?
setVariablesToGetUpdated(updatedVariables.filter((v) => v !== name));
} else if (isMountedCall) {
setVariablesToGetUpdated((prev) => prev.filter((v) => v !== name));
}
Expand Down Expand Up @@ -151,6 +155,7 @@ function DashboardVariableSelection(): JSX.Element | null {
onValueUpdate={onValueUpdate}
variablesToGetUpdated={variablesToGetUpdated}
setVariablesToGetUpdated={setVariablesToGetUpdated}
dependencyData={dependencyData}
/>
))}
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ import { popupContainer } from 'utils/selectPopupContainer';

import { variablePropsToPayloadVariables } from '../utils';
import { SelectItemStyle } from './styles';
import { areArraysEqual } from './util';
import {
areArraysEqual,
checkAPIInvocation,
onUpdateVariableNode,
VariableGraph,
} from './util';

const ALL_SELECT_VALUE = '__ALL__';

const variableRegexPattern = /\{\{\s*?\.([^\s}]+)\s*?\}\}/g;

enum ToggleTagValue {
Only = 'Only',
All = 'All',
Expand All @@ -58,6 +61,11 @@ interface VariableItemProps {
) => void;
variablesToGetUpdated: string[];
setVariablesToGetUpdated: React.Dispatch<React.SetStateAction<string[]>>;
dependencyData: {
order: string[];
graph: VariableGraph;
parentDependencyGraph: VariableGraph;
} | null;
}

const getSelectValue = (
Expand All @@ -80,6 +88,7 @@ function VariableItem({
onValueUpdate,
variablesToGetUpdated,
setVariablesToGetUpdated,
dependencyData,
}: VariableItemProps): JSX.Element {
const [optionsData, setOptionsData] = useState<(string | number | boolean)[]>(
[],
Expand Down Expand Up @@ -112,45 +121,6 @@ function VariableItem({

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
const getOptions = (variablesRes: VariableResponseProps | null): void => {
if (variablesRes && variableData.type === 'QUERY') {
Expand Down Expand Up @@ -240,36 +210,75 @@ function VariableItem({
}
};

const { isLoading } = useQuery(getQueryKey(variableData), {
enabled: variableData && variableData.type === 'QUERY',
queryFn: () =>
dashboardVariablesQuery({
query: variableData.queryValue || '',
variables: variablePropsToPayloadVariables(existingVariables),
}),
refetchOnWindowFocus: false,
onSuccess: (response) => {
getOptions(response.payload);
},
onError: (error: {
details: {
error: string;
};
}) => {
const { details } = error;

if (details.error) {
let message = details.error;
if (details.error.includes('Syntax error:')) {
message =
'Please make sure query is valid and dependent variables are selected';
const { isLoading } = useQuery(
[
REACT_QUERY_KEY.DASHBOARD_BY_ID,
variableData.name || '',
`${minTime}`,
`${maxTime}`,
],
{
enabled:
variableData &&
variableData.type === 'QUERY' &&
checkAPIInvocation(
variablesToGetUpdated,
variableData,
dependencyData?.parentDependencyGraph,
),
queryFn: () =>
dashboardVariablesQuery({
query: variableData.queryValue || '',
variables: variablePropsToPayloadVariables(existingVariables),
}),
refetchOnWindowFocus: false,
onSuccess: (response) => {
getOptions(response.payload);
if (
dependencyData?.parentDependencyGraph[variableData.name || ''].length === 0
) {
const updatedVariables: string[] = [];
onUpdateVariableNode(
variableData.name || '',
dependencyData.graph,
dependencyData.order,
(node) => updatedVariables.push(node),
);
setVariablesToGetUpdated((prev) => [
...prev,
...updatedVariables.filter((v) => v !== variableData.name),
]);
}
setErrorMessage(message);
}
},
onError: (error: {
details: {
error: string;
};
}) => {
const { details } = error;

if (details.error) {
let message = details.error;
if (details.error.includes('Syntax error:')) {
message =
'Please make sure query is valid and dependent variables are selected';
}
setErrorMessage(message);
}
},
},
});
);

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 (
value === ALL_SELECT_VALUE ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { onUpdateVariableNode } from '../util';
import { onUpdateVariableNodeMock } from './mock';

describe('dashboardVariables - utilities and processors', () => {
const { graph, topologicalOrder } = onUpdateVariableNodeMock;

test.each([
{
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: [],
},
])(
'should update variable node when $scenario',
({ nodeToUpdate, expected }) => {
const updatedVariables: string[] = [];

onUpdateVariableNode(nodeToUpdate, graph, topologicalOrder, (node) =>
updatedVariables.push(node),
);

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([]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export const checkAPIInvocationMock = {
variablesToGetUpdated: [],
variableData: {
name: 'k8s_namespace_name',
key: '937ecbae-b24b-4d6d-8cc4-5d5b8d53569b',
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,
},
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'],
},
};

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(),
};
Loading
Loading