Fix/query builder updating (#2962)

* feat: add dynamic table based on query

* fix: group by repeating

* fix: change view when groupBy exist in the list

* feat: add list view for log explorer

* fix: query builder updating

* fix: table scroll

* fix: filters for explorer page (#2959)

---------

Co-authored-by: Prashant Shahi <prashant@signoz.io>
This commit is contained in:
Yevhen Shevchenko 2023-06-23 11:19:53 +03:00 committed by GitHub
parent 522bdf04ef
commit bd18eee662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 491 additions and 278 deletions

View File

@ -70,7 +70,7 @@ function FormAlertRules({
const sq = useMemo(() => mapQueryDataFromApi(initQuery), [initQuery]);
useShareBuilderUrl({ defaultValue: sq });
useShareBuilderUrl(sq);
useEffect(() => {
setAlertDef(initialValue);

View File

@ -1 +0,0 @@
export { LogsExplorerChart } from './LogsExplorerChart';

View File

@ -2,41 +2,33 @@ import Graph from 'components/Graph';
import Spinner from 'components/Spinner';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { getExplorerChartData } from 'lib/explorer/getExplorerChartData';
import { useMemo } from 'react';
import { memo, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
import { CardStyled } from './LogsExplorerChart.styled';
export function LogsExplorerChart(): JSX.Element {
const { stagedQuery } = useQueryBuilder();
function LogsExplorerChart(): JSX.Element {
const { stagedQuery, panelType, isEnabledQuery } = useQueryBuilder();
const { selectedTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const panelTypeParam = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
const { data, isFetching } = useGetQueryRange(
{
query: stagedQuery || initialQueriesMap.metrics,
graphType: panelTypeParam,
graphType: panelType || PANEL_TYPES.LIST,
globalSelectedInterval: selectedTime,
selectedTime: 'GLOBAL_TIME',
},
{
queryKey: [
REACT_QUERY_KEY.GET_QUERY_RANGE,
selectedTime,
stagedQuery,
panelTypeParam,
],
enabled: !!stagedQuery,
queryKey: [REACT_QUERY_KEY.GET_QUERY_RANGE, selectedTime, stagedQuery],
enabled: isEnabledQuery,
},
);
@ -64,3 +56,5 @@ export function LogsExplorerChart(): JSX.Element {
</CardStyled>
);
}
export default memo(LogsExplorerChart);

View File

@ -0,0 +1,3 @@
import { QueryDataV3 } from 'types/api/widgets/getQuery';
export type LogsExplorerListProps = { data: QueryDataV3[]; isLoading: boolean };

View File

@ -0,0 +1,117 @@
import { Card, Typography } from 'antd';
// components
import ListLogView from 'components/Logs/ListLogView';
import RawLogView from 'components/Logs/RawLogView';
import LogsTableView from 'components/Logs/TableView';
import Spinner from 'components/Spinner';
import { LogViewMode } from 'container/LogsTable';
import { Container, Heading } from 'container/LogsTable/styles';
import { contentStyle } from 'container/Trace/Search/config';
import useFontFaceObserver from 'hooks/useFontObserver';
import { memo, useCallback, useMemo, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
// interfaces
import { ILog } from 'types/api/logs/log';
import { LogsExplorerListProps } from './LogsExplorerList.interfaces';
function LogsExplorerList({
data,
isLoading,
}: LogsExplorerListProps): JSX.Element {
const [viewMode] = useState<LogViewMode>('raw');
const [linesPerRow] = useState<number>(20);
const logs: ILog[] = useMemo(() => {
if (data.length > 0 && data[0].list) {
const logs: ILog[] = data[0].list.map((item) => ({
timestamp: +item.timestamp,
...item.data,
}));
return logs;
}
return [];
}, [data]);
useFontFaceObserver(
[
{
family: 'Fira Code',
weight: '300',
},
],
viewMode === 'raw',
{
timeout: 5000,
},
);
// TODO: implement here linesPerRow, mode like in useSelectedLogView
const getItemContent = useCallback(
(index: number): JSX.Element => {
const log = logs[index];
if (viewMode === 'raw') {
return (
<RawLogView
key={log.id}
data={log}
linesPerRow={linesPerRow}
// TODO: write new onClickExpanded logic
onClickExpand={(): void => {}}
/>
);
}
return <ListLogView key={log.id} logData={log} />;
},
[logs, linesPerRow, viewMode],
);
const renderContent = useMemo(() => {
if (viewMode === 'table') {
return (
<LogsTableView
logs={logs}
// TODO: write new selected logic
fields={[]}
linesPerRow={linesPerRow}
// TODO: write new onClickExpanded logic
onClickExpand={(): void => {}}
/>
);
}
return (
<Card bodyStyle={contentStyle}>
<Virtuoso
useWindowScroll
totalCount={logs.length}
itemContent={getItemContent}
/>
</Card>
);
}, [getItemContent, linesPerRow, logs, viewMode]);
if (isLoading) {
return <Spinner height={20} tip="Getting Logs" />;
}
return (
<Container>
{viewMode !== 'table' && (
<Heading>
<Typography.Text>Event</Typography.Text>
</Heading>
)}
{logs.length === 0 && <Typography>No logs lines found</Typography>}
{renderContent}
</Container>
);
}
export default memo(LogsExplorerList);

View File

@ -0,0 +1,6 @@
import { QueryDataV3 } from 'types/api/widgets/getQuery';
export type LogsExplorerTableProps = {
data: QueryDataV3[];
isLoading: boolean;
};

View File

@ -1,44 +0,0 @@
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { QueryTable } from 'container/QueryTable';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
export function LogsExplorerTable(): JSX.Element {
const { stagedQuery } = useQueryBuilder();
const { selectedTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const panelTypeParam = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
const { data, isFetching } = useGetQueryRange(
{
query: stagedQuery || initialQueriesMap.metrics,
graphType: panelTypeParam,
globalSelectedInterval: selectedTime,
selectedTime: 'GLOBAL_TIME',
},
{
queryKey: [
REACT_QUERY_KEY.GET_QUERY_RANGE,
selectedTime,
stagedQuery,
panelTypeParam,
],
enabled: !!stagedQuery,
},
);
return (
<QueryTable
query={stagedQuery || initialQueriesMap.metrics}
queryTableData={data?.payload.data.newResult.data.result || []}
loading={isFetching}
/>
);
}

View File

@ -1 +0,0 @@
export { LogsExplorerTable } from './LogsExplorerTable';

View File

@ -0,0 +1,23 @@
import { initialQueriesMap } from 'constants/queryBuilder';
import { QueryTable } from 'container/QueryTable';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { memo } from 'react';
import { LogsExplorerTableProps } from './LogsExplorerTable.interfaces';
function LogsExplorerTable({
isLoading,
data,
}: LogsExplorerTableProps): JSX.Element {
const { stagedQuery } = useQueryBuilder();
return (
<QueryTable
query={stagedQuery || initialQueriesMap.metrics}
queryTableData={data}
loading={isLoading}
/>
);
}
export default memo(LogsExplorerTable);

View File

@ -1,87 +0,0 @@
import { TabsProps } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
import { LogsExplorerTable } from 'container/LogsExplorerTable';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useUrlQuery from 'hooks/useUrlQuery';
import { useCallback, useEffect, useMemo } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { TabsStyled } from './LogsExplorerViews.styled';
export function LogsExplorerViews(): JSX.Element {
const location = useLocation();
const urlQuery = useUrlQuery();
const history = useHistory();
const { currentQuery } = useQueryBuilder();
const panelTypeParams = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
const isMultipleQueries = useMemo(
() =>
currentQuery.builder.queryData.length > 1 ||
currentQuery.builder.queryFormulas.length > 0,
[currentQuery],
);
const isGroupByExist = useMemo(() => {
const groupByCount: number = currentQuery.builder.queryData.reduce<number>(
(acc, query) => acc + query.groupBy.length,
0,
);
return groupByCount > 0;
}, [currentQuery]);
const tabsItems: TabsProps['items'] = useMemo(
() => [
{
label: 'List View',
key: PANEL_TYPES.LIST,
disabled: isMultipleQueries || isGroupByExist,
},
{ label: 'TimeSeries', key: PANEL_TYPES.TIME_SERIES },
{ label: 'Table', key: PANEL_TYPES.TABLE, children: <LogsExplorerTable /> },
],
[isMultipleQueries, isGroupByExist],
);
const handleChangeView = useCallback(
(panelType: string) => {
urlQuery.set(PANEL_TYPES_QUERY, JSON.stringify(panelType) as GRAPH_TYPES);
const path = `${location.pathname}?${urlQuery}`;
history.push(path);
},
[history, location, urlQuery],
);
const currentTabKey = useMemo(
() =>
Object.values(PANEL_TYPES).includes(panelTypeParams)
? panelTypeParams
: PANEL_TYPES.LIST,
[panelTypeParams],
);
useEffect(() => {
const shouldChangeView = isMultipleQueries || isGroupByExist;
if (panelTypeParams === 'list' && shouldChangeView) {
handleChangeView(PANEL_TYPES.TIME_SERIES);
}
}, [panelTypeParams, isMultipleQueries, isGroupByExist, handleChangeView]);
return (
<div>
<TabsStyled
items={tabsItems}
defaultActiveKey={currentTabKey}
activeKey={currentTabKey}
onChange={handleChangeView}
/>
</div>
);
}

View File

@ -1 +0,0 @@
export { LogsExplorerViews } from './LogsExplorerViews';

View File

@ -0,0 +1,124 @@
import { TabsProps } from 'antd';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { PANEL_TYPES_QUERY } from 'constants/queryBuilderQueryNames';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import LogsExplorerList from 'container/LogsExplorerList';
import LogsExplorerTable from 'container/LogsExplorerTable';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { memo, useCallback, useEffect, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import { TabsStyled } from './LogsExplorerViews.styled';
function LogsExplorerViews(): JSX.Element {
const {
currentQuery,
stagedQuery,
panelType,
isEnabledQuery,
updateAllQueriesOperators,
redirectWithQueryBuilderData,
} = useQueryBuilder();
const { selectedTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { data, isFetching } = useGetQueryRange(
{
query: stagedQuery || initialQueriesMap.metrics,
graphType: panelType || PANEL_TYPES.LIST,
globalSelectedInterval: selectedTime,
selectedTime: 'GLOBAL_TIME',
},
{
queryKey: [REACT_QUERY_KEY.GET_QUERY_RANGE, selectedTime, stagedQuery],
enabled: isEnabledQuery,
},
);
const isMultipleQueries = useMemo(
() =>
currentQuery.builder.queryData.length > 1 ||
currentQuery.builder.queryFormulas.length > 0,
[currentQuery],
);
const isGroupByExist = useMemo(() => {
const groupByCount: number = currentQuery.builder.queryData.reduce<number>(
(acc, query) => acc + query.groupBy.length,
0,
);
return groupByCount > 0;
}, [currentQuery]);
const currentData = useMemo(
() => data?.payload.data.newResult.data.result || [],
[data],
);
const tabsItems: TabsProps['items'] = useMemo(
() => [
{
label: 'List View',
key: PANEL_TYPES.LIST,
disabled: isMultipleQueries || isGroupByExist,
children: <LogsExplorerList data={currentData} isLoading={isFetching} />,
},
{ label: 'TimeSeries', key: PANEL_TYPES.TIME_SERIES },
{
label: 'Table',
key: PANEL_TYPES.TABLE,
children: <LogsExplorerTable data={currentData} isLoading={isFetching} />,
},
],
[isMultipleQueries, isGroupByExist, currentData, isFetching],
);
const handleChangeView = useCallback(
(newPanelType: string) => {
if (newPanelType === panelType) return;
const query = updateAllQueriesOperators(
currentQuery,
newPanelType as GRAPH_TYPES,
DataSource.LOGS,
);
redirectWithQueryBuilderData(query, { [PANEL_TYPES_QUERY]: newPanelType });
},
[
currentQuery,
panelType,
updateAllQueriesOperators,
redirectWithQueryBuilderData,
],
);
useEffect(() => {
const shouldChangeView = isMultipleQueries || isGroupByExist;
if (panelType === 'list' && shouldChangeView) {
handleChangeView(PANEL_TYPES.TIME_SERIES);
}
}, [panelType, isMultipleQueries, isGroupByExist, handleChangeView]);
return (
<div>
<TabsStyled
items={tabsItems}
defaultActiveKey={panelType || PANEL_TYPES.LIST}
activeKey={panelType || PANEL_TYPES.LIST}
onChange={handleChangeView}
/>
</div>
);
}
export default memo(LogsExplorerViews);

View File

@ -58,7 +58,7 @@ function QuerySection({
const { query } = selectedWidget;
useShareBuilderUrl({ defaultValue: query });
useShareBuilderUrl(query);
const handleStageQuery = useCallback(
(updatedQuery: Query): void => {

View File

@ -15,26 +15,36 @@ import { ActionsWrapperStyled } from './QueryBuilder.styled';
export const QueryBuilder = memo(function QueryBuilder({
config,
panelType,
panelType: newPanelType,
actions,
}: QueryBuilderProps): JSX.Element {
const {
currentQuery,
setupInitialDataSource,
addNewBuilderQuery,
addNewFormula,
handleSetPanelType,
handleSetConfig,
panelType,
initialDataSource,
} = useQueryBuilder();
useEffect(() => {
if (config && config.queryVariant === 'static') {
setupInitialDataSource(config.initialDataSource);
}
}, [config, setupInitialDataSource]);
const currentDataSource = useMemo(
() =>
(config && config.queryVariant === 'static' && config.initialDataSource) ||
null,
[config],
);
useEffect(() => {
handleSetPanelType(panelType);
}, [handleSetPanelType, panelType]);
if (currentDataSource !== initialDataSource || newPanelType !== panelType) {
handleSetConfig(newPanelType, currentDataSource);
}
}, [
handleSetConfig,
panelType,
initialDataSource,
currentDataSource,
newPanelType,
]);
const isDisabledQueryButton = useMemo(
() => currentQuery.builder.queryData.length >= MAX_QUERIES,

View File

@ -1,6 +1,5 @@
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
import { initialQueriesMap } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import useUrlQuery from 'hooks/useUrlQuery';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { UseQueryOptions, UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
@ -10,6 +9,7 @@ import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { GlobalReducer } from 'types/reducer/globalTime';
import { useGetCompositeQueryParam } from './useGetCompositeQueryParam';
import { useGetQueryRange } from './useGetQueryRange';
export const useGetWidgetQueryRange = (
@ -19,21 +19,19 @@ export const useGetWidgetQueryRange = (
}: Pick<GetQueryResultsProps, 'graphType' | 'selectedTime'>,
options?: UseQueryOptions<SuccessResponse<MetricRangePayloadProps>, Error>,
): UseQueryResult<SuccessResponse<MetricRangePayloadProps>, Error> => {
const urlQuery = useUrlQuery();
const { selectedTime: globalSelectedInterval } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
const compositeQuery = useGetCompositeQueryParam();
return useGetQueryRange(
{
graphType,
selectedTime,
globalSelectedInterval,
query: JSON.parse(compositeQuery || '{}'),
query: compositeQuery || initialQueriesMap.metrics,
variables: getDashboardVariables(),
},
{

View File

@ -2,6 +2,7 @@ import {
initialAutocompleteData,
initialQueryBuilderFormValuesMap,
mapOfFilters,
PANEL_TYPES,
} from 'constants/queryBuilder';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType';
@ -78,7 +79,7 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
(nextSource: DataSource): void => {
const newOperators = getOperatorsBySourceAndPanelType({
dataSource: nextSource,
panelType,
panelType: panelType || PANEL_TYPES.TIME_SERIES,
});
const entries = Object.entries(
@ -126,28 +127,13 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
const initialOperators = getOperatorsBySourceAndPanelType({
dataSource,
panelType,
panelType: panelType || PANEL_TYPES.TIME_SERIES,
});
if (JSON.stringify(operators) === JSON.stringify(initialOperators)) return;
setOperators(initialOperators);
const isCurrentOperatorAvailableInList = initialOperators
.map((operator) => operator.value)
.includes(aggregateOperator);
if (!isCurrentOperatorAvailableInList) {
handleChangeOperator(initialOperators[0].value);
}
}, [
dataSource,
initialDataSource,
panelType,
operators,
aggregateOperator,
handleChangeOperator,
]);
}, [dataSource, initialDataSource, panelType, operators]);
useEffect(() => {
const additionalFilters = getNewListOfAdditionalFilters(dataSource);

View File

@ -5,11 +5,9 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { useGetCompositeQueryParam } from './useGetCompositeQueryParam';
import { useQueryBuilder } from './useQueryBuilder';
type UseShareBuilderUrlParams = { defaultValue: Query };
export type UseShareBuilderUrlParams = { defaultValue: Query };
export const useShareBuilderUrl = ({
defaultValue,
}: UseShareBuilderUrlParams): void => {
export const useShareBuilderUrl = (defaultQuery: Query): void => {
const { redirectWithQueryBuilderData, resetStagedQuery } = useQueryBuilder();
const urlQuery = useUrlQuery();
@ -17,9 +15,9 @@ export const useShareBuilderUrl = ({
useEffect(() => {
if (!compositeQuery) {
redirectWithQueryBuilderData(defaultValue);
redirectWithQueryBuilderData(defaultQuery);
}
}, [defaultValue, urlQuery, redirectWithQueryBuilderData, compositeQuery]);
}, [defaultQuery, urlQuery, redirectWithQueryBuilderData, compositeQuery]);
useEffect(
() => (): void => {

View File

@ -1,21 +1,32 @@
import { Button, Col, Row } from 'antd';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { LogsExplorerChart } from 'container/LogsExplorerChart';
import { LogsExplorerViews } from 'container/LogsExplorerViews';
import LogsExplorerChart from 'container/LogsExplorerChart';
import LogsExplorerViews from 'container/LogsExplorerViews';
import { QueryBuilder } from 'container/QueryBuilder';
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import { useMemo } from 'react';
import { DataSource } from 'types/common/queryBuilder';
// ** Styles
import { ButtonWrapperStyled, WrapperStyled } from './styles';
function LogsExporer(): JSX.Element {
const { handleRunQuery } = useQueryBuilder();
const { handleRunQuery, updateAllQueriesOperators } = useQueryBuilder();
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
useShareBuilderUrl({ defaultValue: initialQueriesMap.logs });
const defaultValue = useMemo(
() =>
updateAllQueriesOperators(
initialQueriesMap.logs,
PANEL_TYPES.LIST,
DataSource.LOGS,
),
[updateAllQueriesOperators],
);
useShareBuilderUrl(defaultValue);
return (
<WrapperStyled>

View File

@ -37,7 +37,7 @@ function TracesExplorer(): JSX.Element {
[redirectWithCurrentTab],
);
useShareBuilderUrl({ defaultValue: initialQueriesMap.traces });
useShareBuilderUrl(initialQueriesMap.traces);
useEffect(() => {
if (currentUrlTab) return;

View File

@ -52,11 +52,11 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
stagedQuery: initialQueriesMap.metrics,
initialDataSource: null,
panelType: PANEL_TYPES.TIME_SERIES,
isEnabledQuery: false,
handleSetQueryData: () => {},
handleSetFormulaData: () => {},
handleSetQueryItemData: () => {},
handleSetPanelType: () => {},
setupInitialDataSource: () => {},
handleSetConfig: () => {},
removeQueryBuilderEntityByIndex: () => {},
removeQueryTypeItemByIndex: () => {},
addNewBuilderQuery: () => {},
@ -65,6 +65,7 @@ export const QueryBuilderContext = createContext<QueryBuilderContextType>({
redirectWithQueryBuilderData: () => {},
handleRunQuery: () => {},
resetStagedQuery: () => {},
updateAllQueriesOperators: () => initialQueriesMap.metrics,
});
export function QueryBuilderProvider({
@ -75,75 +76,120 @@ export function QueryBuilderProvider({
const location = useLocation();
const compositeQueryParam = useGetCompositeQueryParam();
const { queryType: queryTypeParam, ...queryState } =
compositeQueryParam || initialQueriesMap.metrics;
const [initialDataSource, setInitialDataSource] = useState<DataSource | null>(
null,
);
const [panelType, setPanelType] = useState<GRAPH_TYPES>(
PANEL_TYPES.TIME_SERIES,
);
const [panelType, setPanelType] = useState<GRAPH_TYPES | null>(null);
const [currentQuery, setCurrentQuery] = useState<QueryState>(
initialQueryState,
queryState || initialQueryState,
);
const [stagedQuery, setStagedQuery] = useState<Query | null>(null);
const [queryType, setQueryType] = useState<EQueryType>(
EQueryType.QUERY_BUILDER,
const [queryType, setQueryType] = useState<EQueryType>(queryTypeParam);
const getElementWithActualOperator = useCallback(
(
queryData: IBuilderQuery,
dataSource: DataSource,
currentPanelType: GRAPH_TYPES,
): IBuilderQuery => {
const initialOperators = getOperatorsBySourceAndPanelType({
dataSource,
panelType: currentPanelType,
});
const isCurrentOperatorAvailableInList = initialOperators
.map((operator) => operator.value)
.includes(queryData.aggregateOperator);
if (!isCurrentOperatorAvailableInList) {
return { ...queryData, aggregateOperator: initialOperators[0].value };
}
return queryData;
},
[],
);
const initQueryBuilderData = useCallback(
(query: Query): void => {
const { queryType: newQueryType, ...queryState } = query;
const prepareQueryBuilderData = useCallback(
(query: Query): Query => {
const builder: QueryBuilderData = {
queryData: queryState.builder.queryData.map((item) => ({
queryData: query.builder.queryData.map((item) => ({
...initialQueryBuilderFormValuesMap[
initialDataSource || DataSource.METRICS
],
...item,
})),
queryFormulas: queryState.builder.queryFormulas.map((item) => ({
queryFormulas: query.builder.queryFormulas.map((item) => ({
...initialFormulaBuilderFormValues,
...item,
})),
};
const promql: IPromQLQuery[] = queryState.promql.map((item) => ({
const setupedQueryData = builder.queryData.map((item) => {
const currentElement: IBuilderQuery = {
...item,
groupBy: item.groupBy.map(({ id: _, ...item }) => ({
...item,
id: createIdFromObjectFields(item, baseAutoCompleteIdKeysOrder),
})),
aggregateAttribute: {
...item.aggregateAttribute,
id: createIdFromObjectFields(
item.aggregateAttribute,
baseAutoCompleteIdKeysOrder,
),
},
};
return currentElement;
});
const promql: IPromQLQuery[] = query.promql.map((item) => ({
...initialQueryPromQLData,
...item,
}));
const clickHouse: IClickHouseQuery[] = queryState.clickhouse_sql.map(
(item) => ({
...initialClickHouseData,
...item,
}),
);
const type = newQueryType || EQueryType.QUERY_BUILDER;
const clickHouse: IClickHouseQuery[] = query.clickhouse_sql.map((item) => ({
...initialClickHouseData,
...item,
}));
const newQueryState: QueryState = {
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,
),
},
})),
queryData: setupedQueryData,
},
id: query.id,
};
const nextQuery: Query = {
...newQueryState,
queryType: query.queryType,
};
return nextQuery;
},
[initialDataSource],
);
const initQueryBuilderData = useCallback(
(query: Query): void => {
const { queryType: newQueryType, ...queryState } = prepareQueryBuilderData(
query,
);
const type = newQueryType || EQueryType.QUERY_BUILDER;
const newQueryState: QueryState = {
...queryState,
id: queryState.id,
};
@ -153,7 +199,19 @@ export function QueryBuilderProvider({
setCurrentQuery(newQueryState);
setQueryType(type);
},
[initialDataSource],
[prepareQueryBuilderData],
);
const updateAllQueriesOperators = useCallback(
(query: Query, panelType: GRAPH_TYPES, dataSource: DataSource): Query => {
const queryData = query.builder.queryData.map((item) =>
getElementWithActualOperator(item, dataSource, panelType),
);
return { ...query, builder: { ...query.builder, queryData } };
},
[getElementWithActualOperator],
);
const removeQueryBuilderEntityByIndex = useCallback(
@ -161,11 +219,14 @@ export function QueryBuilderProvider({
setCurrentQuery((prevState) => {
const currentArray: (IBuilderQuery | IBuilderFormula)[] =
prevState.builder[type];
const filteredArray = currentArray.filter((_, i) => index !== i);
return {
...prevState,
builder: {
...prevState.builder,
[type]: currentArray.filter((_, i) => index !== i),
[type]: filteredArray,
},
};
});
@ -199,20 +260,11 @@ export function QueryBuilderProvider({
existNames,
sourceNames: alphabet,
}),
...(initialDataSource
? {
dataSource: initialDataSource,
aggregateOperator: getOperatorsBySourceAndPanelType({
dataSource: initialDataSource,
panelType,
})[0].value,
}
: {}),
};
return newQuery;
},
[initialDataSource, panelType],
[initialDataSource],
);
const createNewBuilderFormula = useCallback((formulas: IBuilderFormula[]) => {
@ -297,12 +349,6 @@ export function QueryBuilderProvider({
});
}, [createNewBuilderFormula]);
const setupInitialDataSource = useCallback(
(newInitialDataSource: DataSource | null) =>
setInitialDataSource(newInitialDataSource),
[],
);
const updateQueryBuilderData: <T>(
arr: T[],
index: number,
@ -377,29 +423,33 @@ export function QueryBuilderProvider({
[updateQueryBuilderData],
);
const handleSetPanelType = useCallback((newPanelType: GRAPH_TYPES) => {
setPanelType(newPanelType);
}, []);
const redirectWithQueryBuilderData = useCallback(
(query: Partial<Query>, searchParams?: Record<string, unknown>) => {
const queryType =
!query.queryType || !Object.values(EQueryType).includes(query.queryType)
? EQueryType.QUERY_BUILDER
: query.queryType;
const builder =
!query.builder || query.builder.queryData.length === 0
? initialQueryState.builder
: query.builder;
const promql =
!query.promql || query.promql.length === 0
? initialQueryState.promql
: query.promql;
const clickhouseSql =
!query.clickhouse_sql || query.clickhouse_sql.length === 0
? initialQueryState.clickhouse_sql
: query.clickhouse_sql;
const currentGeneratedQuery: Query = {
queryType:
!query.queryType || !Object.values(EQueryType).includes(query.queryType)
? EQueryType.QUERY_BUILDER
: query.queryType,
builder:
!query.builder || query.builder.queryData.length === 0
? initialQueryState.builder
: query.builder,
promql:
!query.promql || query.promql.length === 0
? initialQueryState.promql
: query.promql,
clickhouse_sql:
!query.clickhouse_sql || query.clickhouse_sql.length === 0
? initialQueryState.clickhouse_sql
: query.clickhouse_sql,
queryType,
builder,
promql,
clickhouse_sql: clickhouseSql,
id: uuid(),
};
@ -418,6 +468,14 @@ export function QueryBuilderProvider({
[history, location, urlQuery],
);
const handleSetConfig = useCallback(
(newPanelType: GRAPH_TYPES, dataSource: DataSource | null) => {
setPanelType(newPanelType);
setInitialDataSource(dataSource);
},
[],
);
const handleRunQuery = useCallback(() => {
redirectWithQueryBuilderData({ ...currentQuery, queryType });
}, [redirectWithQueryBuilderData, currentQuery, queryType]);
@ -458,17 +516,22 @@ export function QueryBuilderProvider({
[currentQuery, queryType],
);
const isEnabledQuery = useMemo(() => !!stagedQuery && !!panelType, [
stagedQuery,
panelType,
]);
const contextValues: QueryBuilderContextType = useMemo(
() => ({
currentQuery: query,
stagedQuery,
initialDataSource,
panelType,
isEnabledQuery,
handleSetQueryData,
handleSetFormulaData,
handleSetQueryItemData,
handleSetPanelType,
setupInitialDataSource,
handleSetConfig,
removeQueryBuilderEntityByIndex,
removeQueryTypeItemByIndex,
addNewBuilderQuery,
@ -477,17 +540,18 @@ export function QueryBuilderProvider({
redirectWithQueryBuilderData,
handleRunQuery,
resetStagedQuery,
updateAllQueriesOperators,
}),
[
query,
stagedQuery,
initialDataSource,
panelType,
isEnabledQuery,
handleSetQueryData,
handleSetFormulaData,
handleSetQueryItemData,
handleSetPanelType,
setupInitialDataSource,
handleSetConfig,
removeQueryBuilderEntityByIndex,
removeQueryTypeItemByIndex,
addNewBuilderQuery,
@ -496,6 +560,7 @@ export function QueryBuilderProvider({
redirectWithQueryBuilderData,
handleRunQuery,
resetStagedQuery,
updateAllQueriesOperators,
],
);

View File

@ -1,8 +1,12 @@
import { ILog } from '../logs/log';
export interface PayloadProps {
status: 'success' | 'error';
result: QueryData[];
}
type ListItem = { timestamp: string; data: Omit<ILog, 'timestamp'> };
export interface QueryData {
metric: {
[key: string]: string;
@ -20,7 +24,7 @@ export interface SeriesItem {
}
export interface QueryDataV3 {
list: null;
list: ListItem[] | null;
queryName: string;
legend?: string;
series: SeriesItem[] | null;

View File

@ -156,7 +156,8 @@ export type QueryBuilderContextType = {
currentQuery: Query;
stagedQuery: Query | null;
initialDataSource: DataSource | null;
panelType: GRAPH_TYPES;
panelType: GRAPH_TYPES | null;
isEnabledQuery: boolean;
handleSetQueryData: (index: number, queryData: IBuilderQuery) => void;
handleSetFormulaData: (index: number, formulaData: IBuilderFormula) => void;
handleSetQueryItemData: (
@ -164,8 +165,10 @@ export type QueryBuilderContextType = {
type: EQueryType.PROM | EQueryType.CLICKHOUSE,
newQueryData: IPromQLQuery | IClickHouseQuery,
) => void;
handleSetPanelType: (newPanelType: GRAPH_TYPES) => void;
setupInitialDataSource: (newInitialDataSource: DataSource | null) => void;
handleSetConfig: (
newPanelType: GRAPH_TYPES,
dataSource: DataSource | null,
) => void;
removeQueryBuilderEntityByIndex: (
type: keyof QueryBuilderData,
index: number,
@ -183,6 +186,11 @@ export type QueryBuilderContextType = {
) => void;
handleRunQuery: () => void;
resetStagedQuery: () => void;
updateAllQueriesOperators: (
queryData: Query,
panelType: GRAPH_TYPES,
dataSource: DataSource,
) => Query;
};
export type QueryAdditionalFilter = {