feat: handle unkown metric in panel query (#8083)
* feat: handle unkown metric in panel query * feat: added handling with type empty and key present or not * feat: added test cases * feat: added comment to better explain the logic * feat: fixed operator list for unkown metric --------- Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
This commit is contained in:
@@ -425,3 +425,79 @@ export const metricsEmptyTimeAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [];
|
||||
|
||||
export const metricsUnknownTimeAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.MAX,
|
||||
label: 'Max',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MIN,
|
||||
label: 'Min',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.SUM,
|
||||
label: 'Sum',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.AVG,
|
||||
label: 'Avg',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.COUNT,
|
||||
label: 'Count',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.RATE,
|
||||
label: 'Rate',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.INCREASE,
|
||||
label: 'Increase',
|
||||
},
|
||||
];
|
||||
|
||||
export const metricsUnknownSpaceAggregateOperatorOptions: SelectOption<
|
||||
string,
|
||||
string
|
||||
>[] = [
|
||||
{
|
||||
value: MetricAggregateOperator.SUM,
|
||||
label: 'Sum',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.AVG,
|
||||
label: 'Avg',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MIN,
|
||||
label: 'Min',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.MAX,
|
||||
label: 'Max',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P50,
|
||||
label: 'P50',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P75,
|
||||
label: 'P75',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P90,
|
||||
label: 'P90',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P95,
|
||||
label: 'P95',
|
||||
},
|
||||
{
|
||||
value: MetricAggregateOperator.P99,
|
||||
label: 'P99',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { ATTRIBUTE_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
|
||||
import { useQueryBuilder } from '../useQueryBuilder';
|
||||
import { useQueryOperations } from '../useQueryBuilderOperations';
|
||||
|
||||
// Mock the useQueryBuilder hook
|
||||
jest.mock('../useQueryBuilder', () => ({
|
||||
useQueryBuilder: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('useQueryBuilderOperations - Empty Aggregate Attribute Type', () => {
|
||||
const mockHandleSetQueryData = jest.fn();
|
||||
const mockHandleSetFormulaData = jest.fn();
|
||||
const mockRemoveQueryBuilderEntityByIndex = jest.fn();
|
||||
const mockSetLastUsedQuery = jest.fn();
|
||||
const mockRedirectWithQueryBuilderData = jest.fn();
|
||||
|
||||
const defaultMockQuery: IBuilderQuery = {
|
||||
dataSource: DataSource.METRICS,
|
||||
aggregateOperator: MetricAggregateOperator.AVG,
|
||||
aggregateAttribute: {
|
||||
key: 'test_metric',
|
||||
dataType: DataTypes.Float64,
|
||||
type: ATTRIBUTE_TYPES.GAUGE,
|
||||
} as BaseAutocompleteData,
|
||||
timeAggregation: MetricAggregateOperator.AVG,
|
||||
spaceAggregation: '',
|
||||
having: [],
|
||||
limit: null,
|
||||
queryName: 'test_query',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
groupBy: [],
|
||||
orderBy: [],
|
||||
stepInterval: 60,
|
||||
expression: '',
|
||||
disabled: false,
|
||||
reduceTo: 'avg',
|
||||
legend: '',
|
||||
};
|
||||
|
||||
const setupMockQueryBuilder = (): void => {
|
||||
(useQueryBuilder as jest.Mock).mockReturnValue({
|
||||
handleSetQueryData: mockHandleSetQueryData,
|
||||
handleSetFormulaData: mockHandleSetFormulaData,
|
||||
removeQueryBuilderEntityByIndex: mockRemoveQueryBuilderEntityByIndex,
|
||||
setLastUsedQuery: mockSetLastUsedQuery,
|
||||
redirectWithQueryBuilderData: mockRedirectWithQueryBuilderData,
|
||||
panelType: 'time_series',
|
||||
currentQuery: {
|
||||
builder: {
|
||||
queryData: [defaultMockQuery, defaultMockQuery],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderHookWithProps = (
|
||||
props = {},
|
||||
): { current: ReturnType<typeof useQueryOperations> } => {
|
||||
const { result } = renderHook(() =>
|
||||
useQueryOperations({
|
||||
query: defaultMockQuery,
|
||||
index: 0,
|
||||
entityVersion: ENTITY_VERSION_V4,
|
||||
...props,
|
||||
}),
|
||||
);
|
||||
return result;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setupMockQueryBuilder();
|
||||
});
|
||||
|
||||
describe('handleChangeAggregatorAttribute', () => {
|
||||
it('should set AVG operators when type is empty but key is present - unkown metric', () => {
|
||||
const result = renderHookWithProps();
|
||||
const newAttribute: BaseAutocompleteData = {
|
||||
key: 'new_metric',
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.handleChangeAggregatorAttribute(newAttribute);
|
||||
});
|
||||
|
||||
expect(mockHandleSetQueryData).toHaveBeenCalledWith(
|
||||
0,
|
||||
expect.objectContaining({
|
||||
aggregateAttribute: newAttribute,
|
||||
aggregateOperator: MetricAggregateOperator.AVG,
|
||||
timeAggregation: MetricAggregateOperator.AVG,
|
||||
spaceAggregation: MetricAggregateOperator.AVG,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should set COUNT/RATE/SUM operators when both type and key are empty', () => {
|
||||
const result = renderHookWithProps();
|
||||
const newAttribute: BaseAutocompleteData = {
|
||||
key: '',
|
||||
dataType: DataTypes.Float64,
|
||||
type: '',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.handleChangeAggregatorAttribute(newAttribute);
|
||||
});
|
||||
|
||||
expect(mockHandleSetQueryData).toHaveBeenCalledWith(
|
||||
0,
|
||||
expect.objectContaining({
|
||||
aggregateAttribute: newAttribute,
|
||||
aggregateOperator: MetricAggregateOperator.COUNT,
|
||||
timeAggregation: MetricAggregateOperator.RATE,
|
||||
spaceAggregation: MetricAggregateOperator.SUM,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
metricsGaugeSpaceAggregateOperatorOptions,
|
||||
metricsHistogramSpaceAggregateOperatorOptions,
|
||||
metricsSumSpaceAggregateOperatorOptions,
|
||||
metricsUnknownSpaceAggregateOperatorOptions,
|
||||
metricsUnknownTimeAggregateOperatorOptions,
|
||||
} from 'constants/queryBuilderOperators';
|
||||
import {
|
||||
listViewInitialLogQuery,
|
||||
@@ -21,6 +23,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { getMetricsOperatorsByAttributeType } from 'lib/newQueryBuilder/getMetricsOperatorsByAttributeType';
|
||||
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
@@ -145,12 +148,18 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
|
||||
const handleMetricAggregateAtributeTypes = useCallback(
|
||||
(aggregateAttribute: BaseAutocompleteData): any => {
|
||||
const newOperators = getMetricsOperatorsByAttributeType({
|
||||
dataSource: DataSource.METRICS,
|
||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||
aggregateAttributeType:
|
||||
(aggregateAttribute.type as ATTRIBUTE_TYPES) || ATTRIBUTE_TYPES.GAUGE,
|
||||
});
|
||||
// operators for unknown metric
|
||||
const isUnknownMetric =
|
||||
isEmpty(aggregateAttribute.type) && !isEmpty(aggregateAttribute.key);
|
||||
|
||||
const newOperators = isUnknownMetric
|
||||
? metricsUnknownTimeAggregateOperatorOptions
|
||||
: getMetricsOperatorsByAttributeType({
|
||||
dataSource: DataSource.METRICS,
|
||||
panelType: panelType || PANEL_TYPES.TIME_SERIES,
|
||||
aggregateAttributeType:
|
||||
(aggregateAttribute.type as ATTRIBUTE_TYPES) || ATTRIBUTE_TYPES.GAUGE,
|
||||
});
|
||||
|
||||
switch (aggregateAttribute.type) {
|
||||
case ATTRIBUTE_TYPES.SUM:
|
||||
@@ -168,7 +177,7 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
setSpaceAggregationOptions(metricsHistogramSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
default:
|
||||
setSpaceAggregationOptions(metricsGaugeSpaceAggregateOperatorOptions);
|
||||
setSpaceAggregationOptions(metricsUnknownSpaceAggregateOperatorOptions);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -202,6 +211,21 @@ export const useQueryOperations: UseQueryOperations = ({
|
||||
}
|
||||
|
||||
newQuery.spaceAggregation = '';
|
||||
|
||||
// Handled query with unknown metric to avoid 400 and 500 errors
|
||||
// With metric value typed and not available then - time - 'avg', space - 'avg'
|
||||
// If not typed - time - 'rate', space - 'sum', op - 'count'
|
||||
if (isEmpty(newQuery.aggregateAttribute.type)) {
|
||||
if (!isEmpty(newQuery.aggregateAttribute.key)) {
|
||||
newQuery.aggregateOperator = MetricAggregateOperator.AVG;
|
||||
newQuery.timeAggregation = MetricAggregateOperator.AVG;
|
||||
newQuery.spaceAggregation = MetricAggregateOperator.AVG;
|
||||
} else {
|
||||
newQuery.aggregateOperator = MetricAggregateOperator.COUNT;
|
||||
newQuery.timeAggregation = MetricAggregateOperator.RATE;
|
||||
newQuery.spaceAggregation = MetricAggregateOperator.SUM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
|
||||
@@ -3,7 +3,11 @@ import {
|
||||
metricsOperatorsByType,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { metricsEmptyTimeAggregateOperatorOptions } from 'constants/queryBuilderOperators';
|
||||
import {
|
||||
metricsEmptyTimeAggregateOperatorOptions,
|
||||
metricsUnknownTimeAggregateOperatorOptions,
|
||||
} from 'constants/queryBuilderOperators';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
@@ -27,5 +31,9 @@ export const getMetricsOperatorsByAttributeType = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (dataSource === DataSource.METRICS && isEmpty(aggregateAttributeType)) {
|
||||
return metricsUnknownTimeAggregateOperatorOptions;
|
||||
}
|
||||
|
||||
return metricsEmptyTimeAggregateOperatorOptions;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user