mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-16 17:45:55 +08:00
fix: query builder edit mode doesn't send correct payload (#2804)
* fix: where clause getting values * fix: group by filter custom option * fix: id for group by and aggregate filters * fix: repeating values * refactor: group by uniq items * fix: removing source key * fix: keep where clause filter on operator change * chore: clean up for console log and additional variables --------- Co-authored-by: Chintan Sudani <46838508+techchintan@users.noreply.github.com> Co-authored-by: Vishal Sharma <makeavish786@gmail.com> Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
6614cd31c1
commit
342e94d093
@ -1,6 +1,8 @@
|
|||||||
import { ApiV3Instance } from 'api';
|
import { ApiV3Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder';
|
||||||
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
@ -10,7 +12,6 @@ import {
|
|||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
IQueryAutocompleteResponse,
|
IQueryAutocompleteResponse,
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
export const getAggregateAttribute = async ({
|
export const getAggregateAttribute = async ({
|
||||||
aggregateOperator,
|
aggregateOperator,
|
||||||
@ -31,8 +32,10 @@ export const getAggregateAttribute = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const payload: BaseAutocompleteData[] =
|
const payload: BaseAutocompleteData[] =
|
||||||
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
|
response.data.data.attributeKeys?.map(({ id: _, ...item }) => ({
|
||||||
[];
|
...item,
|
||||||
|
id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
||||||
|
})) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { ApiV3Instance } from 'api';
|
import { ApiV3Instance } from 'api';
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
import { AxiosError, AxiosResponse } from 'axios';
|
import { AxiosError, AxiosResponse } from 'axios';
|
||||||
|
import { baseAutoCompleteIdKeysOrder } from 'constants/queryBuilder';
|
||||||
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import createQueryParams from 'lib/createQueryParams';
|
import createQueryParams from 'lib/createQueryParams';
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
// ** Types
|
// ** Types
|
||||||
@ -9,7 +11,6 @@ import {
|
|||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
IQueryAutocompleteResponse,
|
IQueryAutocompleteResponse,
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
export const getAggregateKeys = async ({
|
export const getAggregateKeys = async ({
|
||||||
aggregateOperator,
|
aggregateOperator,
|
||||||
@ -33,8 +34,10 @@ export const getAggregateKeys = async ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const payload: BaseAutocompleteData[] =
|
const payload: BaseAutocompleteData[] =
|
||||||
response.data.data.attributeKeys?.map((item) => ({ ...item, id: uuid() })) ||
|
response.data.data.attributeKeys?.map(({ id: _, ...item }) => ({
|
||||||
[];
|
...item,
|
||||||
|
id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
||||||
|
})) || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||||
import { LocalDataType } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import {
|
||||||
|
BaseAutocompleteData,
|
||||||
|
LocalDataType,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import {
|
import {
|
||||||
HavingForm,
|
HavingForm,
|
||||||
IBuilderFormula,
|
IBuilderFormula,
|
||||||
@ -35,7 +38,13 @@ import {
|
|||||||
export const MAX_FORMULAS = 20;
|
export const MAX_FORMULAS = 20;
|
||||||
export const MAX_QUERIES = 26;
|
export const MAX_QUERIES = 26;
|
||||||
|
|
||||||
export const selectValueDivider = '--';
|
export const idDivider = '--';
|
||||||
|
export const selectValueDivider = '__';
|
||||||
|
|
||||||
|
export const baseAutoCompleteIdKeysOrder: (keyof Omit<
|
||||||
|
BaseAutocompleteData,
|
||||||
|
'id'
|
||||||
|
>)[] = ['key', 'dataType', 'type', 'isColumn'];
|
||||||
|
|
||||||
export const formulasNames: string[] = Array.from(
|
export const formulasNames: string[] = Array.from(
|
||||||
Array(MAX_FORMULAS),
|
Array(MAX_FORMULAS),
|
||||||
@ -90,7 +99,7 @@ export const initialHavingValues: HavingForm = {
|
|||||||
value: [],
|
value: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const initialAggregateAttribute: IBuilderQuery['aggregateAttribute'] = {
|
export const initialAutocompleteData: BaseAutocompleteData = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
dataType: null,
|
dataType: null,
|
||||||
key: '',
|
key: '',
|
||||||
@ -102,7 +111,7 @@ export const initialQueryBuilderFormValues: IBuilderQuery = {
|
|||||||
dataSource: DataSource.METRICS,
|
dataSource: DataSource.METRICS,
|
||||||
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||||
aggregateOperator: MetricAggregateOperator.NOOP,
|
aggregateOperator: MetricAggregateOperator.NOOP,
|
||||||
aggregateAttribute: initialAggregateAttribute,
|
aggregateAttribute: initialAutocompleteData,
|
||||||
filters: { items: [], op: 'AND' },
|
filters: { items: [], op: 'AND' },
|
||||||
expression: createNewBuilderItemName({
|
expression: createNewBuilderItemName({
|
||||||
existNames: [],
|
existNames: [],
|
||||||
|
@ -3,19 +3,25 @@ import { AutoComplete, Spin } from 'antd';
|
|||||||
// ** Api
|
// ** Api
|
||||||
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
|
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
|
||||||
import {
|
import {
|
||||||
initialAggregateAttribute,
|
baseAutoCompleteIdKeysOrder,
|
||||||
|
idDivider,
|
||||||
|
initialAutocompleteData,
|
||||||
QueryBuilderKeys,
|
QueryBuilderKeys,
|
||||||
selectValueDivider,
|
selectValueDivider,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
import useDebounce from 'hooks/useDebounce';
|
import useDebounce from 'hooks/useDebounce';
|
||||||
import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue';
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import { memo, useCallback, useMemo, useState } from 'react';
|
import { memo, useCallback, useMemo, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
import {
|
||||||
|
AutocompleteType,
|
||||||
|
BaseAutocompleteData,
|
||||||
|
DataType,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
import { ExtendedSelectOption } from 'types/common/select';
|
import { ExtendedSelectOption } from 'types/common/select';
|
||||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
// ** Types
|
// ** Types
|
||||||
@ -27,7 +33,7 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
}: AgregatorFilterProps): JSX.Element {
|
}: AgregatorFilterProps): JSX.Element {
|
||||||
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
const [optionsData, setOptionsData] = useState<ExtendedSelectOption[]>([]);
|
||||||
const debouncedValue = useDebounce(query.aggregateAttribute.key, 300);
|
const debouncedValue = useDebounce(query.aggregateAttribute.key, 300);
|
||||||
const { data, isFetching } = useQuery(
|
const { isFetching } = useQuery(
|
||||||
[
|
[
|
||||||
QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE,
|
QueryBuilderKeys.GET_AGGREGATE_ATTRIBUTE,
|
||||||
debouncedValue,
|
debouncedValue,
|
||||||
@ -44,18 +50,17 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
enabled: !!query.aggregateOperator && !!query.dataSource,
|
enabled: !!query.aggregateOperator && !!query.dataSource,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
const options: ExtendedSelectOption[] =
|
const options: ExtendedSelectOption[] =
|
||||||
data?.payload?.attributeKeys?.map((item) => ({
|
data?.payload?.attributeKeys?.map(({ id: _, ...item }) => ({
|
||||||
label: transformStringWithPrefix({
|
label: transformStringWithPrefix({
|
||||||
str: item.key,
|
str: item.key,
|
||||||
prefix: item.type || '',
|
prefix: item.type || '',
|
||||||
condition: !item.isColumn,
|
condition: !item.isColumn,
|
||||||
}),
|
}),
|
||||||
value: `${transformStringWithPrefix({
|
value: `${item.key}${selectValueDivider}${createIdFromObjectFields(
|
||||||
str: item.key,
|
item,
|
||||||
prefix: item.type || '',
|
baseAutoCompleteIdKeysOrder,
|
||||||
condition: !item.isColumn,
|
)}`,
|
||||||
})}${selectValueDivider}${item.id || uuid()}`,
|
key: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
||||||
key: item.id || uuid(),
|
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
setOptionsData(options);
|
setOptionsData(options);
|
||||||
@ -70,15 +75,23 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
|||||||
): void => {
|
): void => {
|
||||||
const currentOption = option as ExtendedSelectOption;
|
const currentOption = option as ExtendedSelectOption;
|
||||||
|
|
||||||
const { key } = getFilterObjectValue(value);
|
if (currentOption.key) {
|
||||||
|
const [key, dataType, type, isColumn] = currentOption.key.split(idDivider);
|
||||||
|
const attribute: BaseAutocompleteData = {
|
||||||
|
key,
|
||||||
|
dataType: dataType as DataType,
|
||||||
|
type: type as AutocompleteType,
|
||||||
|
isColumn: isColumn === 'true',
|
||||||
|
};
|
||||||
|
|
||||||
const currentAttributeObj = data?.payload?.attributeKeys?.find(
|
onChange(attribute);
|
||||||
(item) => currentOption.key === item.id,
|
} else {
|
||||||
) || { ...initialAggregateAttribute, key };
|
const attribute = { ...initialAutocompleteData, key: value };
|
||||||
|
|
||||||
onChange(currentAttributeObj);
|
onChange(attribute);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[data, onChange],
|
[onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
import { Select, Spin } from 'antd';
|
import { Select, Spin } from 'antd';
|
||||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||||
// ** Constants
|
// ** Constants
|
||||||
import { QueryBuilderKeys, selectValueDivider } from 'constants/queryBuilder';
|
import {
|
||||||
|
idDivider,
|
||||||
|
initialAutocompleteData,
|
||||||
|
QueryBuilderKeys,
|
||||||
|
selectValueDivider,
|
||||||
|
} from 'constants/queryBuilder';
|
||||||
import useDebounce from 'hooks/useDebounce';
|
import useDebounce from 'hooks/useDebounce';
|
||||||
import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue';
|
|
||||||
// ** Components
|
// ** Components
|
||||||
// ** Helpers
|
// ** Helpers
|
||||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { isEqual, uniqWith } from 'lodash-es';
|
||||||
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import {
|
||||||
|
AutocompleteType,
|
||||||
|
BaseAutocompleteData,
|
||||||
|
DataType,
|
||||||
|
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import { SelectOption } from 'types/common/select';
|
import { SelectOption } from 'types/common/select';
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||||
import { GroupByFilterProps } from './GroupByFilter.interfaces';
|
import { GroupByFilterProps } from './GroupByFilter.interfaces';
|
||||||
@ -32,7 +40,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
|
|
||||||
const debouncedValue = useDebounce(searchText, 300);
|
const debouncedValue = useDebounce(searchText, 300);
|
||||||
|
|
||||||
const { data, isFetching } = useQuery(
|
const { isFetching } = useQuery(
|
||||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedValue, isFocused],
|
[QueryBuilderKeys.GET_AGGREGATE_KEYS, debouncedValue, isFocused],
|
||||||
async () =>
|
async () =>
|
||||||
getAggregateKeys({
|
getAggregateKeys({
|
||||||
@ -65,7 +73,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
str: item.key,
|
str: item.key,
|
||||||
prefix: item.type || '',
|
prefix: item.type || '',
|
||||||
condition: !item.isColumn,
|
condition: !item.isColumn,
|
||||||
})}${selectValueDivider}${item.id || uuid()}`,
|
})}${selectValueDivider}${item.id}`,
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
setOptionsData(options);
|
setOptionsData(options);
|
||||||
@ -87,36 +95,32 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (values: SelectOption<string, string>[]): void => {
|
const handleChange = (values: SelectOption<string, string>[]): void => {
|
||||||
const responseKeys = data?.payload?.attributeKeys || [];
|
|
||||||
|
|
||||||
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
|
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
|
||||||
const [currentValue, id] = item.value.split(selectValueDivider);
|
const [currentValue, id] = item.value.split(selectValueDivider);
|
||||||
const { key, type, isColumn } = getFilterObjectValue(currentValue);
|
if (id && id.includes(idDivider)) {
|
||||||
|
const [key, dataType, type, isColumn] = id.split(idDivider);
|
||||||
|
|
||||||
const existGroupQuery = query.groupBy.find((group) => group.id === id);
|
return {
|
||||||
|
id,
|
||||||
if (existGroupQuery) {
|
key,
|
||||||
return existGroupQuery;
|
dataType: dataType as DataType,
|
||||||
|
type: type as AutocompleteType,
|
||||||
|
isColumn: isColumn === 'true',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const existGroupResponse = responseKeys.find((group) => group.id === id);
|
return { ...initialAutocompleteData, key: currentValue };
|
||||||
|
|
||||||
if (existGroupResponse) {
|
|
||||||
return existGroupResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: uuid(),
|
|
||||||
isColumn,
|
|
||||||
key,
|
|
||||||
dataType: null,
|
|
||||||
type,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onChange(groupByValues);
|
const result = uniqWith(groupByValues, isEqual);
|
||||||
|
|
||||||
|
onChange(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearSearch = useCallback(() => {
|
||||||
|
setSearchText('');
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentValues: SelectOption<string, string>[] = query.groupBy.map(
|
const currentValues: SelectOption<string, string>[] = query.groupBy.map(
|
||||||
(item) => ({
|
(item) => ({
|
||||||
@ -129,7 +133,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
str: item.key,
|
str: item.key,
|
||||||
prefix: item.type || '',
|
prefix: item.type || '',
|
||||||
condition: !item.isColumn,
|
condition: !item.isColumn,
|
||||||
})}${selectValueDivider}${item.id || uuid()}`,
|
})}${selectValueDivider}${item.id}`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -147,6 +151,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
|||||||
filterOption={false}
|
filterOption={false}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
|
onDeselect={clearSearch}
|
||||||
options={optionsData}
|
options={optionsData}
|
||||||
value={localValues}
|
value={localValues}
|
||||||
labelInValue
|
labelInValue
|
||||||
|
@ -5,9 +5,11 @@ import {
|
|||||||
KeyboardEvent,
|
KeyboardEvent,
|
||||||
ReactElement,
|
ReactElement,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
import {
|
import {
|
||||||
IBuilderQuery,
|
IBuilderQuery,
|
||||||
TagFilter,
|
TagFilter,
|
||||||
@ -44,7 +46,11 @@ function QueryBuilderSearch({
|
|||||||
searchKey,
|
searchKey,
|
||||||
} = useAutoComplete(query);
|
} = useAutoComplete(query);
|
||||||
|
|
||||||
const { sourceKeys } = useFetchKeysAndValues(searchValue, query, searchKey);
|
const { sourceKeys, handleRemoveSourceKey } = useFetchKeysAndValues(
|
||||||
|
searchValue,
|
||||||
|
query,
|
||||||
|
searchKey,
|
||||||
|
);
|
||||||
|
|
||||||
const onTagRender = ({
|
const onTagRender = ({
|
||||||
value,
|
value,
|
||||||
@ -94,6 +100,14 @@ function QueryBuilderSearch({
|
|||||||
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeselect = useCallback(
|
||||||
|
(deselectedItem: string) => {
|
||||||
|
handleClearTag(deselectedItem);
|
||||||
|
handleRemoveSourceKey(deselectedItem);
|
||||||
|
},
|
||||||
|
[handleClearTag, handleRemoveSourceKey],
|
||||||
|
);
|
||||||
|
|
||||||
const isMetricsDataSource = useMemo(
|
const isMetricsDataSource = useMemo(
|
||||||
() => query.dataSource === DataSource.METRICS,
|
() => query.dataSource === DataSource.METRICS,
|
||||||
[query.dataSource],
|
[query.dataSource],
|
||||||
@ -106,9 +120,12 @@ function QueryBuilderSearch({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
const initialTagFilters: TagFilter = { items: [], op: 'AND' };
|
||||||
|
const initialSourceKeys = query.filters.items.map(
|
||||||
|
(item) => item.key as BaseAutocompleteData,
|
||||||
|
);
|
||||||
initialTagFilters.items = tags.map((tag) => {
|
initialTagFilters.items = tags.map((tag) => {
|
||||||
const { tagKey, tagOperator, tagValue } = getTagToken(tag);
|
const { tagKey, tagOperator, tagValue } = getTagToken(tag);
|
||||||
const filterAttribute = sourceKeys.find(
|
const filterAttribute = [...initialSourceKeys, ...sourceKeys].find(
|
||||||
(key) => key.key === getRemovePrefixFromKey(tagKey),
|
(key) => key.key === getRemovePrefixFromKey(tagKey),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@ -128,7 +145,7 @@ function QueryBuilderSearch({
|
|||||||
});
|
});
|
||||||
onChange(initialTagFilters);
|
onChange(initialTagFilters);
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
}, [sourceKeys, tags]);
|
}, [sourceKeys]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
@ -146,7 +163,7 @@ function QueryBuilderSearch({
|
|||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
onDeselect={handleClearTag}
|
onDeselect={handleDeselect}
|
||||||
onInputKeyDown={onInputKeyDownHandler}
|
onInputKeyDown={onInputKeyDownHandler}
|
||||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||||
>
|
>
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
import { isEqual, uniqWith } from 'lodash-es';
|
import { isEqual, uniqWith } from 'lodash-es';
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useDebounce } from 'react-use';
|
import { useDebounce } from 'react-use';
|
||||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
@ -20,6 +20,7 @@ type IuseFetchKeysAndValues = {
|
|||||||
results: string[];
|
results: string[];
|
||||||
isFetching: boolean;
|
isFetching: boolean;
|
||||||
sourceKeys: BaseAutocompleteData[];
|
sourceKeys: BaseAutocompleteData[];
|
||||||
|
handleRemoveSourceKey: (newSourceKey: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,6 +128,12 @@ export const useFetchKeysAndValues = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemoveSourceKey = useCallback((sourceKey: string) => {
|
||||||
|
setSourceKeys((prevState) =>
|
||||||
|
prevState.filter((item) => item.key !== sourceKey),
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// creates a ref to the fetch function so that it doesn't change on every render
|
// creates a ref to the fetch function so that it doesn't change on every render
|
||||||
const clearFetcher = useRef(handleFetchOption).current;
|
const clearFetcher = useRef(handleFetchOption).current;
|
||||||
|
|
||||||
@ -155,5 +162,6 @@ export const useFetchKeysAndValues = (
|
|||||||
results,
|
results,
|
||||||
isFetching,
|
isFetching,
|
||||||
sourceKeys,
|
sourceKeys,
|
||||||
|
handleRemoveSourceKey,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
initialAggregateAttribute,
|
initialAutocompleteData,
|
||||||
initialQueryBuilderFormValues,
|
initialQueryBuilderFormValues,
|
||||||
mapOfFilters,
|
mapOfFilters,
|
||||||
} from 'constants/queryBuilder';
|
} from 'constants/queryBuilder';
|
||||||
@ -44,9 +44,8 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
|
|||||||
aggregateOperator: value,
|
aggregateOperator: value,
|
||||||
having: [],
|
having: [],
|
||||||
limit: null,
|
limit: null,
|
||||||
filters: { items: [], op: 'AND' },
|
|
||||||
...(shouldResetAggregateAttribute
|
...(shouldResetAggregateAttribute
|
||||||
? { aggregateAttribute: initialAggregateAttribute }
|
? { aggregateAttribute: initialAutocompleteData }
|
||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import * as Papa from 'papaparse';
|
import * as Papa from 'papaparse';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
|
|
||||||
type IUseTag = {
|
type IUseTag = {
|
||||||
@ -30,7 +30,19 @@ export const useTag = (
|
|||||||
query: IBuilderQuery,
|
query: IBuilderQuery,
|
||||||
setSearchKey: (value: string) => void,
|
setSearchKey: (value: string) => void,
|
||||||
): IUseTag => {
|
): IUseTag => {
|
||||||
const [tags, setTags] = useState<string[]>([]);
|
const initTagsData = useMemo(
|
||||||
|
() =>
|
||||||
|
(query?.filters?.items || []).map((ele) => {
|
||||||
|
if (isInNInOperator(getOperatorFromValue(ele.op))) {
|
||||||
|
const csvString = Papa.unparse([ele.value]);
|
||||||
|
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${csvString}`;
|
||||||
|
}
|
||||||
|
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${ele.value}`;
|
||||||
|
}),
|
||||||
|
[query.filters],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [tags, setTags] = useState<string[]>(initTagsData);
|
||||||
|
|
||||||
const updateTag = (value: string): void => {
|
const updateTag = (value: string): void => {
|
||||||
const newTags = tags?.filter((item: string) => item !== value);
|
const newTags = tags?.filter((item: string) => item !== value);
|
||||||
@ -61,16 +73,8 @@ export const useTag = (
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialTags = (query?.filters?.items || []).map((ele) => {
|
setTags(initTagsData);
|
||||||
if (isInNInOperator(getOperatorFromValue(ele.op))) {
|
}, [initTagsData]);
|
||||||
const csvString = Papa.unparse([ele.value]);
|
|
||||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${csvString}`;
|
|
||||||
}
|
|
||||||
return `${ele.key?.key} ${getOperatorFromValue(ele.op)} ${ele.value}`;
|
|
||||||
});
|
|
||||||
setTags(initialTags);
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { handleAddTag, handleClearTag, tags, updateTag };
|
return { handleAddTag, handleClearTag, tags, updateTag };
|
||||||
};
|
};
|
||||||
|
6
frontend/src/lib/createIdFromObjectFields.ts
Normal file
6
frontend/src/lib/createIdFromObjectFields.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { idDivider } from 'constants/queryBuilder';
|
||||||
|
|
||||||
|
export const createIdFromObjectFields = <T, K extends keyof T>(
|
||||||
|
obj: T,
|
||||||
|
orderedKeys: K[],
|
||||||
|
): string => orderedKeys.map((key) => obj[key]).join(idDivider);
|
@ -1,33 +0,0 @@
|
|||||||
import { TYPE_ADDON_REGEXP } from 'constants/regExp';
|
|
||||||
import {
|
|
||||||
AutocompleteType,
|
|
||||||
BaseAutocompleteData,
|
|
||||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
|
||||||
|
|
||||||
const getType = (str: string): AutocompleteType | null => {
|
|
||||||
const types: AutocompleteType[] = ['tag', 'resource'];
|
|
||||||
|
|
||||||
let currentType: AutocompleteType | null = null;
|
|
||||||
|
|
||||||
types.forEach((type) => {
|
|
||||||
if (str.includes(type)) {
|
|
||||||
currentType = type;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return currentType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getFilterObjectValue = (
|
|
||||||
value: string,
|
|
||||||
): Omit<BaseAutocompleteData, 'dataType' | 'id'> => {
|
|
||||||
const type = getType(value);
|
|
||||||
|
|
||||||
if (!type) {
|
|
||||||
return { isColumn: true, key: value, type: null };
|
|
||||||
}
|
|
||||||
|
|
||||||
const splittedValue = value.split(TYPE_ADDON_REGEXP);
|
|
||||||
|
|
||||||
return { isColumn: false, key: splittedValue[1], type };
|
|
||||||
};
|
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
alphabet,
|
alphabet,
|
||||||
|
baseAutoCompleteIdKeysOrder,
|
||||||
formulasNames,
|
formulasNames,
|
||||||
initialClickHouseData,
|
initialClickHouseData,
|
||||||
initialFormulaBuilderFormValues,
|
initialFormulaBuilderFormValues,
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
|
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
|
import { createIdFromObjectFields } from 'lib/createIdFromObjectFields';
|
||||||
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||||
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
|
||||||
import { replaceIncorrectObjectFields } from 'lib/replaceIncorrectObjectFields';
|
import { replaceIncorrectObjectFields } from 'lib/replaceIncorrectObjectFields';
|
||||||
@ -100,6 +102,7 @@ export function QueryBuilderProvider({
|
|||||||
|
|
||||||
const initQueryBuilderData = useCallback((query: Partial<Query>): void => {
|
const initQueryBuilderData = useCallback((query: Partial<Query>): void => {
|
||||||
const { queryType, ...queryState } = query;
|
const { queryType, ...queryState } = query;
|
||||||
|
|
||||||
const builder: QueryBuilderData = {
|
const builder: QueryBuilderData = {
|
||||||
queryData: queryState.builder
|
queryData: queryState.builder
|
||||||
? queryState.builder.queryData.map((item) => ({
|
? queryState.builder.queryData.map((item) => ({
|
||||||
@ -129,7 +132,27 @@ export function QueryBuilderProvider({
|
|||||||
}))
|
}))
|
||||||
: initialQuery.clickhouse_sql;
|
: initialQuery.clickhouse_sql;
|
||||||
|
|
||||||
setCurrentQuery({ builder, clickhouse_sql: clickHouse, promql });
|
setCurrentQuery({
|
||||||
|
clickhouse_sql: clickHouse,
|
||||||
|
promql,
|
||||||
|
builder: {
|
||||||
|
...builder,
|
||||||
|
queryData: builder.queryData.map((q) => ({
|
||||||
|
...q,
|
||||||
|
groupBy: q.groupBy.map(({ id: _, ...item }) => ({
|
||||||
|
...item,
|
||||||
|
id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
|
||||||
|
})),
|
||||||
|
aggregateAttribute: {
|
||||||
|
...q.aggregateAttribute,
|
||||||
|
id: createIdFromObjectFields(
|
||||||
|
q.aggregateAttribute,
|
||||||
|
baseAutoCompleteIdKeysOrder,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
setQueryType(queryType || EQueryType.QUERY_BUILDER);
|
setQueryType(queryType || EQueryType.QUERY_BUILDER);
|
||||||
}, []);
|
}, []);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user