mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 18:55:57 +08:00
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:
parent
522bdf04ef
commit
bd18eee662
@ -70,7 +70,7 @@ function FormAlertRules({
|
||||
|
||||
const sq = useMemo(() => mapQueryDataFromApi(initQuery), [initQuery]);
|
||||
|
||||
useShareBuilderUrl({ defaultValue: sq });
|
||||
useShareBuilderUrl(sq);
|
||||
|
||||
useEffect(() => {
|
||||
setAlertDef(initialValue);
|
||||
|
@ -1 +0,0 @@
|
||||
export { LogsExplorerChart } from './LogsExplorerChart';
|
@ -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);
|
@ -0,0 +1,3 @@
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
export type LogsExplorerListProps = { data: QueryDataV3[]; isLoading: boolean };
|
117
frontend/src/container/LogsExplorerList/index.tsx
Normal file
117
frontend/src/container/LogsExplorerList/index.tsx
Normal 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);
|
@ -0,0 +1,6 @@
|
||||
import { QueryDataV3 } from 'types/api/widgets/getQuery';
|
||||
|
||||
export type LogsExplorerTableProps = {
|
||||
data: QueryDataV3[];
|
||||
isLoading: boolean;
|
||||
};
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { LogsExplorerTable } from './LogsExplorerTable';
|
23
frontend/src/container/LogsExplorerTable/index.tsx
Normal file
23
frontend/src/container/LogsExplorerTable/index.tsx
Normal 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);
|
@ -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>
|
||||
);
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { LogsExplorerViews } from './LogsExplorerViews';
|
124
frontend/src/container/LogsExplorerViews/index.tsx
Normal file
124
frontend/src/container/LogsExplorerViews/index.tsx
Normal 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);
|
@ -58,7 +58,7 @@ function QuerySection({
|
||||
|
||||
const { query } = selectedWidget;
|
||||
|
||||
useShareBuilderUrl({ defaultValue: query });
|
||||
useShareBuilderUrl(query);
|
||||
|
||||
const handleStageQuery = useCallback(
|
||||
(updatedQuery: Query): void => {
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
},
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 => {
|
||||
|
@ -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>
|
||||
|
@ -37,7 +37,7 @@ function TracesExplorer(): JSX.Element {
|
||||
[redirectWithCurrentTab],
|
||||
);
|
||||
|
||||
useShareBuilderUrl({ defaultValue: initialQueriesMap.traces });
|
||||
useShareBuilderUrl(initialQueriesMap.traces);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentUrlTab) return;
|
||||
|
@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user