mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-15 03:25:53 +08:00
feat: add the traces view for the traces explorer (#2966)
* feat: add dynamic table based on query * feat: add the list view for the traces explorer * feat: add the list view for the traces explorer * feat: add the list view for the traces explorer * feat: add the table view for the traces explorer * feat: add the table view for the traces explorer * feat: add the trace view for the traces explorer * feat: update the traces view tab for the traces explorer page * feat: update the traces view --------- Co-authored-by: Yevhen Shevchenko <y.shevchenko@seedium.io> Co-authored-by: Nazarenko19 <danil.nazarenko2000@gmail.com> Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
This commit is contained in:
parent
78da014b52
commit
10a3a6d3e5
@ -232,8 +232,8 @@ export const PANEL_TYPES: Record<PanelTypeKeys, GRAPH_TYPES> = {
|
|||||||
VALUE: 'value',
|
VALUE: 'value',
|
||||||
TABLE: 'table',
|
TABLE: 'table',
|
||||||
LIST: 'list',
|
LIST: 'list',
|
||||||
EMPTY_WIDGET: 'EMPTY_WIDGET',
|
|
||||||
TRACE: 'trace',
|
TRACE: 'trace',
|
||||||
|
EMPTY_WIDGET: 'EMPTY_WIDGET',
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IQueryBuilderState = 'search';
|
export type IQueryBuilderState = 'search';
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
||||||
import { Button, Select } from 'antd';
|
import { Button, Select } from 'antd';
|
||||||
import { Pagination } from 'hooks/queryPagination';
|
import { DEFAULT_PER_PAGE_OPTIONS, Pagination } from 'hooks/queryPagination';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useMemo } from 'react';
|
||||||
|
|
||||||
import { defaultSelectStyle, ITEMS_PER_PAGE_OPTIONS } from './config';
|
import { defaultSelectStyle } from './config';
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
function Controls({
|
function Controls({
|
||||||
offset = 0,
|
offset = 0,
|
||||||
|
perPageOptions = DEFAULT_PER_PAGE_OPTIONS,
|
||||||
isLoading,
|
isLoading,
|
||||||
totalCount,
|
totalCount,
|
||||||
countPerPage,
|
countPerPage,
|
||||||
@ -51,7 +52,7 @@ function Controls({
|
|||||||
value={countPerPage}
|
value={countPerPage}
|
||||||
onChange={handleCountItemsPerPageChange}
|
onChange={handleCountItemsPerPageChange}
|
||||||
>
|
>
|
||||||
{ITEMS_PER_PAGE_OPTIONS.map((count) => (
|
{perPageOptions.map((count) => (
|
||||||
<Select.Option
|
<Select.Option
|
||||||
key={count}
|
key={count}
|
||||||
value={count}
|
value={count}
|
||||||
@ -64,10 +65,12 @@ function Controls({
|
|||||||
|
|
||||||
Controls.defaultProps = {
|
Controls.defaultProps = {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
perPageOptions: DEFAULT_PER_PAGE_OPTIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ControlsProps {
|
export interface ControlsProps {
|
||||||
offset?: Pagination['offset'];
|
offset?: Pagination['offset'];
|
||||||
|
perPageOptions?: number[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
countPerPage: Pagination['limit'];
|
countPerPage: Pagination['limit'];
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
@ -40,6 +40,7 @@ export const Query = memo(function Query({
|
|||||||
const {
|
const {
|
||||||
operators,
|
operators,
|
||||||
isMetricsDataSource,
|
isMetricsDataSource,
|
||||||
|
isTracePanelType,
|
||||||
listOfAdditionalFilters,
|
listOfAdditionalFilters,
|
||||||
handleChangeAggregatorAttribute,
|
handleChangeAggregatorAttribute,
|
||||||
handleChangeDataSource,
|
handleChangeDataSource,
|
||||||
@ -352,6 +353,7 @@ export const Query = memo(function Query({
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
{!isTracePanelType && (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
|
<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
|
||||||
<Row gutter={[0, 11]} justify="space-between">
|
<Row gutter={[0, 11]} justify="space-between">
|
||||||
@ -359,6 +361,7 @@ export const Query = memo(function Query({
|
|||||||
</Row>
|
</Row>
|
||||||
</AdditionalFiltersToggler>
|
</AdditionalFiltersToggler>
|
||||||
</Col>
|
</Col>
|
||||||
|
)}
|
||||||
<Row style={{ width: '100%' }}>
|
<Row style={{ width: '100%' }}>
|
||||||
<Input
|
<Input
|
||||||
onChange={handleChangeQueryLegend}
|
onChange={handleChangeQueryLegend}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { TableProps } from 'antd';
|
import { TableProps } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
@ -11,4 +12,5 @@ export type QueryTableProps = Omit<
|
|||||||
queryTableData: QueryDataV3[];
|
queryTableData: QueryDataV3[];
|
||||||
query: Query;
|
query: Query;
|
||||||
renderActionCell?: (record: RowData) => ReactNode;
|
renderActionCell?: (record: RowData) => ReactNode;
|
||||||
|
modifyColumns?: (columns: ColumnsType<RowData>) => ColumnsType<RowData>;
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,7 @@ export function QueryTable({
|
|||||||
queryTableData,
|
queryTableData,
|
||||||
query,
|
query,
|
||||||
renderActionCell,
|
renderActionCell,
|
||||||
|
modifyColumns,
|
||||||
...props
|
...props
|
||||||
}: QueryTableProps): JSX.Element {
|
}: QueryTableProps): JSX.Element {
|
||||||
const { columns, dataSource } = useMemo(
|
const { columns, dataSource } = useMemo(
|
||||||
@ -39,9 +40,13 @@ export function QueryTable({
|
|||||||
return currentColumns;
|
return currentColumns;
|
||||||
}, [columns]);
|
}, [columns]);
|
||||||
|
|
||||||
|
const tableColumns = modifyColumns
|
||||||
|
? modifyColumns(modifiedColumns)
|
||||||
|
: modifiedColumns;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizeTable
|
<ResizeTable
|
||||||
columns={modifiedColumns}
|
columns={tableColumns}
|
||||||
tableLayout="fixed"
|
tableLayout="fixed"
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
scroll={{ x: true }}
|
scroll={{ x: true }}
|
||||||
|
@ -33,9 +33,9 @@ function TimeSeriesViewContainer({
|
|||||||
queryKey: [
|
queryKey: [
|
||||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
globalSelectedTime,
|
globalSelectedTime,
|
||||||
stagedQuery,
|
|
||||||
maxTime,
|
maxTime,
|
||||||
minTime,
|
minTime,
|
||||||
|
stagedQuery,
|
||||||
],
|
],
|
||||||
enabled: !!stagedQuery,
|
enabled: !!stagedQuery,
|
||||||
},
|
},
|
||||||
|
@ -1,25 +1,51 @@
|
|||||||
import Controls from 'container/Controls';
|
import Controls, { ControlsProps } from 'container/Controls';
|
||||||
|
import OptionsMenu from 'container/OptionsMenu';
|
||||||
|
import { OptionsMenuConfig } from 'container/OptionsMenu/types';
|
||||||
|
import useQueryPagination from 'hooks/queryPagination/useQueryPagination';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { Container } from './styles';
|
import { Container } from './styles';
|
||||||
|
|
||||||
function TraceExplorerControls(): JSX.Element | null {
|
function TraceExplorerControls({
|
||||||
const handleCountItemsPerPageChange = (): void => {};
|
isLoading,
|
||||||
const handleNavigatePrevious = (): void => {};
|
totalCount,
|
||||||
const handleNavigateNext = (): void => {};
|
perPageOptions,
|
||||||
|
config,
|
||||||
|
}: TraceExplorerControlsProps): JSX.Element | null {
|
||||||
|
const {
|
||||||
|
pagination,
|
||||||
|
handleCountItemsPerPageChange,
|
||||||
|
handleNavigateNext,
|
||||||
|
handleNavigatePrevious,
|
||||||
|
} = useQueryPagination(totalCount, perPageOptions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
{config && <OptionsMenu config={{ addColumn: config?.addColumn }} />}
|
||||||
|
|
||||||
<Controls
|
<Controls
|
||||||
isLoading={false}
|
isLoading={isLoading}
|
||||||
totalCount={0}
|
totalCount={totalCount}
|
||||||
countPerPage={25}
|
offset={pagination.offset}
|
||||||
handleNavigatePrevious={handleNavigatePrevious}
|
countPerPage={pagination.limit}
|
||||||
handleNavigateNext={handleNavigateNext}
|
perPageOptions={perPageOptions}
|
||||||
handleCountItemsPerPageChange={handleCountItemsPerPageChange}
|
handleCountItemsPerPageChange={handleCountItemsPerPageChange}
|
||||||
|
handleNavigateNext={handleNavigateNext}
|
||||||
|
handleNavigatePrevious={handleNavigatePrevious}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TraceExplorerControls.defaultProps = {
|
||||||
|
config: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
type TraceExplorerControlsProps = Pick<
|
||||||
|
ControlsProps,
|
||||||
|
'isLoading' | 'totalCount' | 'perPageOptions'
|
||||||
|
> & {
|
||||||
|
config?: OptionsMenuConfig | null;
|
||||||
|
};
|
||||||
|
|
||||||
export default memo(TraceExplorerControls);
|
export default memo(TraceExplorerControls);
|
||||||
|
@ -3,6 +3,7 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
|||||||
import { QueryBuilder } from 'container/QueryBuilder';
|
import { QueryBuilder } from 'container/QueryBuilder';
|
||||||
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
import { useGetPanelTypesQueryParam } from 'hooks/queryBuilder/useGetPanelTypesQueryParam';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { memo } from 'react';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { ButtonWrapper, Container } from './styles';
|
import { ButtonWrapper, Container } from './styles';
|
||||||
@ -10,7 +11,7 @@ import { ButtonWrapper, Container } from './styles';
|
|||||||
function QuerySection(): JSX.Element {
|
function QuerySection(): JSX.Element {
|
||||||
const { handleRunQuery } = useQueryBuilder();
|
const { handleRunQuery } = useQueryBuilder();
|
||||||
|
|
||||||
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.TIME_SERIES);
|
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@ -32,4 +33,4 @@ function QuerySection(): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default QuerySection;
|
export default memo(QuerySection);
|
||||||
|
49
frontend/src/container/TracesExplorer/TracesView/configs.tsx
Normal file
49
frontend/src/container/TracesExplorer/TracesView/configs.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Typography } from 'antd';
|
||||||
|
import { ColumnsType } from 'antd/es/table';
|
||||||
|
import ROUTES from 'constants/routes';
|
||||||
|
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
|
||||||
|
import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination';
|
||||||
|
import { generatePath } from 'react-router-dom';
|
||||||
|
import { ListItem } from 'types/api/widgets/getQuery';
|
||||||
|
|
||||||
|
export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS];
|
||||||
|
|
||||||
|
export const columns: ColumnsType<ListItem['data']> = [
|
||||||
|
{
|
||||||
|
title: 'Root Service Name',
|
||||||
|
dataIndex: 'subQuery.serviceName',
|
||||||
|
key: 'serviceName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Root Operation Name',
|
||||||
|
dataIndex: 'subQuery.name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Root Duration (in ms)',
|
||||||
|
dataIndex: 'subQuery.durationNano',
|
||||||
|
key: 'durationNano',
|
||||||
|
render: (duration: number): JSX.Element => (
|
||||||
|
<Typography>{getMs(String(duration))}ms</Typography>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'No of Spans',
|
||||||
|
dataIndex: 'span_count',
|
||||||
|
key: 'span_count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'TraceID',
|
||||||
|
dataIndex: 'traceID',
|
||||||
|
key: 'traceID',
|
||||||
|
render: (traceID: string): JSX.Element => (
|
||||||
|
<Typography.Link
|
||||||
|
href={generatePath(ROUTES.TRACE_DETAIL, {
|
||||||
|
id: traceID,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{traceID}
|
||||||
|
</Typography.Link>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
87
frontend/src/container/TracesExplorer/TracesView/index.tsx
Normal file
87
frontend/src/container/TracesExplorer/TracesView/index.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import Typography from 'antd/es/typography/Typography';
|
||||||
|
import { ResizeTable } from 'components/ResizeTable';
|
||||||
|
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||||
|
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import { Pagination, URL_PAGINATION } from 'hooks/queryPagination';
|
||||||
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
|
import TraceExplorerControls from '../Controls';
|
||||||
|
import { columns, PER_PAGE_OPTIONS } from './configs';
|
||||||
|
import { ActionsContainer, Container } from './styles';
|
||||||
|
|
||||||
|
function TracesView(): JSX.Element {
|
||||||
|
const { stagedQuery, panelType } = useQueryBuilder();
|
||||||
|
|
||||||
|
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||||
|
AppState,
|
||||||
|
GlobalReducer
|
||||||
|
>((state) => state.globalTime);
|
||||||
|
|
||||||
|
const { queryData: paginationQueryData } = useUrlQueryData<Pagination>(
|
||||||
|
URL_PAGINATION,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data, isLoading } = useGetQueryRange(
|
||||||
|
{
|
||||||
|
query: stagedQuery || initialQueriesMap.traces,
|
||||||
|
graphType: panelType || PANEL_TYPES.TRACE,
|
||||||
|
selectedTime: 'GLOBAL_TIME',
|
||||||
|
globalSelectedInterval: globalSelectedTime,
|
||||||
|
params: {
|
||||||
|
dataSource: 'traces',
|
||||||
|
},
|
||||||
|
tableParams: {
|
||||||
|
pagination: paginationQueryData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
queryKey: [
|
||||||
|
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||||
|
globalSelectedTime,
|
||||||
|
maxTime,
|
||||||
|
minTime,
|
||||||
|
stagedQuery,
|
||||||
|
panelType,
|
||||||
|
paginationQueryData,
|
||||||
|
],
|
||||||
|
enabled: !!stagedQuery && panelType === PANEL_TYPES.TRACE,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const responseData = data?.payload?.data?.newResult?.data?.result[0]?.list;
|
||||||
|
const tableData = useMemo(
|
||||||
|
() => responseData?.map((listItem) => listItem.data),
|
||||||
|
[responseData],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<ActionsContainer>
|
||||||
|
<Typography>
|
||||||
|
Showing up to X of the slowest traces form the selected time range
|
||||||
|
</Typography>
|
||||||
|
<TraceExplorerControls
|
||||||
|
isLoading={isLoading}
|
||||||
|
totalCount={responseData?.length || 0}
|
||||||
|
perPageOptions={PER_PAGE_OPTIONS}
|
||||||
|
/>
|
||||||
|
</ActionsContainer>
|
||||||
|
<ResizeTable
|
||||||
|
loading={isLoading}
|
||||||
|
columns={columns}
|
||||||
|
tableLayout="fixed"
|
||||||
|
dataSource={tableData}
|
||||||
|
scroll={{ x: true }}
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TracesView;
|
13
frontend/src/container/TracesExplorer/TracesView/styles.ts
Normal file
13
frontend/src/container/TracesExplorer/TracesView/styles.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ActionsContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
@ -122,6 +122,10 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
|
|||||||
[query.dataSource],
|
[query.dataSource],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isTracePanelType = useMemo(() => panelType === PANEL_TYPES.TRACE, [
|
||||||
|
panelType,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (initialDataSource && dataSource !== initialDataSource) return;
|
if (initialDataSource && dataSource !== initialDataSource) return;
|
||||||
|
|
||||||
@ -142,6 +146,7 @@ export const useQueryOperations: UseQueryOperations = ({ query, index }) => {
|
|||||||
}, [dataSource, aggregateOperator, getNewListOfAdditionalFilters]);
|
}, [dataSource, aggregateOperator, getNewListOfAdditionalFilters]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
isTracePanelType,
|
||||||
isMetricsDataSource,
|
isMetricsDataSource,
|
||||||
operators,
|
operators,
|
||||||
listOfAdditionalFilters,
|
listOfAdditionalFilters,
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
import { Pagination } from './types';
|
|
||||||
|
|
||||||
export const URL_PAGINATION = 'pagination';
|
export const URL_PAGINATION = 'pagination';
|
||||||
|
|
||||||
export const defaultPaginationConfig: Pagination = {
|
export const DEFAULT_PER_PAGE_OPTIONS: number[] = [25, 50, 100, 200];
|
||||||
offset: 0,
|
|
||||||
limit: 25,
|
|
||||||
};
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export interface Pagination {
|
export interface Pagination {
|
||||||
offset: number;
|
offset: number;
|
||||||
limit: 25 | 50 | 100 | 200;
|
limit: number;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,23 @@
|
|||||||
import { ControlsProps } from 'container/Controls';
|
import { ControlsProps } from 'container/Controls';
|
||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { defaultPaginationConfig, URL_PAGINATION } from './config';
|
import { DEFAULT_PER_PAGE_OPTIONS, URL_PAGINATION } from './config';
|
||||||
import { Pagination } from './types';
|
import { Pagination } from './types';
|
||||||
import { checkIsValidPaginationData } from './utils';
|
import {
|
||||||
|
checkIsValidPaginationData,
|
||||||
|
getDefaultPaginationConfig,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
const useQueryPagination = (
|
||||||
|
totalCount: number,
|
||||||
|
perPageOptions: number[] = DEFAULT_PER_PAGE_OPTIONS,
|
||||||
|
): UseQueryPagination => {
|
||||||
|
const defaultPaginationConfig = useMemo(
|
||||||
|
() => getDefaultPaginationConfig(perPageOptions),
|
||||||
|
[perPageOptions],
|
||||||
|
);
|
||||||
|
|
||||||
const useQueryPagination = (totalCount: number): UseQueryPagination => {
|
|
||||||
const {
|
const {
|
||||||
query: paginationQuery,
|
query: paginationQuery,
|
||||||
queryData: paginationQueryData,
|
queryData: paginationQueryData,
|
||||||
@ -45,12 +56,19 @@ const useQueryPagination = (totalCount: number): UseQueryPagination => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isValidPaginationData = checkIsValidPaginationData(
|
const isValidPaginationData = checkIsValidPaginationData(
|
||||||
paginationQueryData || defaultPaginationConfig,
|
paginationQueryData || defaultPaginationConfig,
|
||||||
|
perPageOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (paginationQuery && isValidPaginationData) return;
|
if (paginationQuery && isValidPaginationData) return;
|
||||||
|
|
||||||
redirectWithCurrentPagination(defaultPaginationConfig);
|
redirectWithCurrentPagination(defaultPaginationConfig);
|
||||||
}, [paginationQuery, paginationQueryData, redirectWithCurrentPagination]);
|
}, [
|
||||||
|
defaultPaginationConfig,
|
||||||
|
perPageOptions,
|
||||||
|
paginationQuery,
|
||||||
|
paginationQueryData,
|
||||||
|
redirectWithCurrentPagination,
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pagination: paginationQueryData || defaultPaginationConfig,
|
pagination: paginationQueryData || defaultPaginationConfig,
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
|
import { DEFAULT_PER_PAGE_OPTIONS } from './config';
|
||||||
import { Pagination } from './types';
|
import { Pagination } from './types';
|
||||||
|
|
||||||
export const checkIsValidPaginationData = ({
|
export const checkIsValidPaginationData = (
|
||||||
limit,
|
{ limit, offset }: Pagination,
|
||||||
offset,
|
perPageOptions: number[],
|
||||||
}: Pagination): boolean =>
|
): boolean =>
|
||||||
Boolean(
|
Boolean(
|
||||||
limit &&
|
Number.isInteger(limit) &&
|
||||||
(limit === 25 || limit === 50 || limit === 100 || limit === 200) &&
|
limit > 0 &&
|
||||||
offset &&
|
offset >= 0 &&
|
||||||
offset > 0,
|
perPageOptions.find((option) => option === limit),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getDefaultPaginationConfig = (
|
||||||
|
perPageOptions = DEFAULT_PER_PAGE_OPTIONS,
|
||||||
|
): Pagination => ({
|
||||||
|
offset: 0,
|
||||||
|
limit: perPageOptions[0],
|
||||||
|
});
|
||||||
|
@ -10,7 +10,7 @@ const useUrlQueryData = <T>(
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
|
|
||||||
const query = urlQuery.get(queryKey);
|
const query = useMemo(() => urlQuery.get(queryKey), [queryKey, urlQuery]);
|
||||||
|
|
||||||
const queryData: T = useMemo(() => (query ? JSON.parse(query) : defaultData), [
|
const queryData: T = useMemo(() => (query ? JSON.parse(query) : defaultData), [
|
||||||
query,
|
query,
|
||||||
|
@ -15,12 +15,16 @@ export const getOperatorsBySourceAndPanelType = ({
|
|||||||
}: GetQueryOperatorsParams): SelectOption<string, string>[] => {
|
}: GetQueryOperatorsParams): SelectOption<string, string>[] => {
|
||||||
let operatorsByDataSource = mapOfOperators[dataSource];
|
let operatorsByDataSource = mapOfOperators[dataSource];
|
||||||
|
|
||||||
if (panelType === PANEL_TYPES.LIST) {
|
if (panelType === PANEL_TYPES.LIST || panelType === PANEL_TYPES.TRACE) {
|
||||||
operatorsByDataSource = operatorsByDataSource.filter(
|
operatorsByDataSource = operatorsByDataSource.filter(
|
||||||
(operator) => operator.value === StringOperators.NOOP,
|
(operator) => operator.value === StringOperators.NOOP,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (dataSource !== DataSource.METRICS && panelType !== PANEL_TYPES.LIST) {
|
if (
|
||||||
|
dataSource !== DataSource.METRICS &&
|
||||||
|
panelType !== PANEL_TYPES.LIST &&
|
||||||
|
panelType !== PANEL_TYPES.TRACE
|
||||||
|
) {
|
||||||
operatorsByDataSource = operatorsByDataSource.filter(
|
operatorsByDataSource = operatorsByDataSource.filter(
|
||||||
(operator) => operator.value !== StringOperators.NOOP,
|
(operator) => operator.value !== StringOperators.NOOP,
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { GetQueryResultsProps } from 'store/actions/dashboard/getQueryResults';
|
||||||
import {
|
import {
|
||||||
MapData,
|
MapData,
|
||||||
MapQueryDataToApiResult,
|
MapQueryDataToApiResult,
|
||||||
@ -6,6 +7,7 @@ import {
|
|||||||
export const mapQueryDataToApi = <Data extends MapData, Key extends keyof Data>(
|
export const mapQueryDataToApi = <Data extends MapData, Key extends keyof Data>(
|
||||||
data: Data[],
|
data: Data[],
|
||||||
nameField: Key,
|
nameField: Key,
|
||||||
|
tableParams?: GetQueryResultsProps['tableParams'],
|
||||||
): MapQueryDataToApiResult<Record<string, Data>> => {
|
): MapQueryDataToApiResult<Record<string, Data>> => {
|
||||||
const newLegendMap: Record<string, string> = {};
|
const newLegendMap: Record<string, string> = {};
|
||||||
|
|
||||||
@ -14,6 +16,10 @@ export const mapQueryDataToApi = <Data extends MapData, Key extends keyof Data>(
|
|||||||
...acc,
|
...acc,
|
||||||
[query[nameField] as string]: {
|
[query[nameField] as string]: {
|
||||||
...query,
|
...query,
|
||||||
|
...tableParams?.pagination,
|
||||||
|
...(tableParams?.selectColumns
|
||||||
|
? { selectColumns: tableParams?.selectColumns }
|
||||||
|
: null),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { QueryTableProps } from 'container/QueryTable/QueryTable.intefaces';
|
|||||||
import { toCapitalize } from 'lib/toCapitalize';
|
import { toCapitalize } from 'lib/toCapitalize';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { QueryDataV3, SeriesItem } from 'types/api/widgets/getQuery';
|
import { ListItem, QueryDataV3, SeriesItem } from 'types/api/widgets/getQuery';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
type CreateTableDataFromQueryParams = Pick<
|
type CreateTableDataFromQueryParams = Pick<
|
||||||
@ -47,6 +47,10 @@ type GetDynamicColumns = (
|
|||||||
query: Query,
|
query: Query,
|
||||||
) => DynamicColumns;
|
) => DynamicColumns;
|
||||||
|
|
||||||
|
type ListItemData = ListItem['data'];
|
||||||
|
type ListItemKey = keyof ListItemData;
|
||||||
|
type SeriesItemLabels = SeriesItem['labels'];
|
||||||
|
|
||||||
const isFormula = (queryName: string): boolean =>
|
const isFormula = (queryName: string): boolean =>
|
||||||
FORMULA_REGEXP.test(queryName);
|
FORMULA_REGEXP.test(queryName);
|
||||||
|
|
||||||
@ -72,12 +76,45 @@ const prepareColumnTitle = (title: string): string => {
|
|||||||
return toCapitalize(title);
|
return toCapitalize(title);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createLabels = <T extends ListItemData | SeriesItemLabels>(
|
||||||
|
labels: T,
|
||||||
|
label: keyof T,
|
||||||
|
dynamicColumns: DynamicColumns,
|
||||||
|
): void => {
|
||||||
|
if (isColumnExist(label as string, dynamicColumns)) return;
|
||||||
|
if (isFormula(label as string)) return;
|
||||||
|
|
||||||
|
const labelValue = labels[label];
|
||||||
|
|
||||||
|
const isNumber = !Number.isNaN(parseFloat(String(labelValue)));
|
||||||
|
|
||||||
|
const fieldObj: DynamicColumn = {
|
||||||
|
key: label as string,
|
||||||
|
data: [],
|
||||||
|
type: 'field',
|
||||||
|
sortable: isNumber,
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamicColumns.push(fieldObj);
|
||||||
|
};
|
||||||
|
|
||||||
const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => {
|
const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => {
|
||||||
const dynamicColumns: DynamicColumns = [];
|
const dynamicColumns: DynamicColumns = [];
|
||||||
|
|
||||||
queryTableData.forEach((currentQuery) => {
|
queryTableData.forEach((currentQuery) => {
|
||||||
if (!currentQuery.series) return;
|
if (currentQuery.list) {
|
||||||
|
currentQuery.list.forEach((listItem) => {
|
||||||
|
Object.keys(listItem.data).forEach((label) => {
|
||||||
|
createLabels<ListItemData>(
|
||||||
|
listItem.data,
|
||||||
|
label as ListItemKey,
|
||||||
|
dynamicColumns,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentQuery.series) {
|
||||||
if (!isColumnExist('timestamp', dynamicColumns)) {
|
if (!isColumnExist('timestamp', dynamicColumns)) {
|
||||||
dynamicColumns.push({
|
dynamicColumns.push({
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
@ -89,21 +126,7 @@ const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => {
|
|||||||
|
|
||||||
currentQuery.series.forEach((seria) => {
|
currentQuery.series.forEach((seria) => {
|
||||||
Object.keys(seria.labels).forEach((label) => {
|
Object.keys(seria.labels).forEach((label) => {
|
||||||
if (isColumnExist(label, dynamicColumns)) return;
|
createLabels<SeriesItemLabels>(seria.labels, label, dynamicColumns);
|
||||||
if (isFormula(label)) return;
|
|
||||||
|
|
||||||
const labelValue = seria.labels[label];
|
|
||||||
|
|
||||||
const isNumber = !Number.isNaN(parseFloat(labelValue));
|
|
||||||
|
|
||||||
const fieldObj: DynamicColumn = {
|
|
||||||
key: label,
|
|
||||||
data: [],
|
|
||||||
type: 'field',
|
|
||||||
sortable: isNumber,
|
|
||||||
};
|
|
||||||
|
|
||||||
dynamicColumns.push(fieldObj);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -124,6 +147,7 @@ const getDynamicColumns: GetDynamicColumns = (queryTableData, query) => {
|
|||||||
};
|
};
|
||||||
dynamicColumns.push(operatorColumn);
|
dynamicColumns.push(operatorColumn);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return dynamicColumns;
|
return dynamicColumns;
|
||||||
@ -194,22 +218,47 @@ const fillDataFromSeria = (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fillDataFromList = (
|
||||||
|
listItem: ListItem,
|
||||||
|
columns: DynamicColumns,
|
||||||
|
): void => {
|
||||||
|
columns.forEach((column) => {
|
||||||
|
if (isFormula(column.key as string)) return;
|
||||||
|
|
||||||
|
Object.keys(listItem.data).forEach((label) => {
|
||||||
|
if (column.key === label) {
|
||||||
|
if (listItem.data[label as ListItemKey]) {
|
||||||
|
column.data.push(listItem.data[label as ListItemKey] as string | number);
|
||||||
|
} else {
|
||||||
|
column.data.push('N/A');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const fillColumnsData: FillColumnData = (queryTableData, cols, query) => {
|
const fillColumnsData: FillColumnData = (queryTableData, cols, query) => {
|
||||||
const fields = cols.filter((item) => item.type === 'field');
|
const fields = cols.filter((item) => item.type === 'field');
|
||||||
const operators = cols.filter((item) => item.type === 'operator');
|
const operators = cols.filter((item) => item.type === 'operator');
|
||||||
const resultColumns = [...fields, ...operators];
|
const resultColumns = [...fields, ...operators];
|
||||||
|
|
||||||
queryTableData.forEach((currentQuery) => {
|
queryTableData.forEach((currentQuery) => {
|
||||||
if (!currentQuery.series) return;
|
|
||||||
|
|
||||||
const currentOperator = getQueryOperator(
|
const currentOperator = getQueryOperator(
|
||||||
query.builder.queryData,
|
query.builder.queryData,
|
||||||
currentQuery.queryName,
|
currentQuery.queryName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (currentQuery.series) {
|
||||||
currentQuery.series.forEach((seria) => {
|
currentQuery.series.forEach((seria) => {
|
||||||
fillDataFromSeria(seria, resultColumns, currentOperator);
|
fillDataFromSeria(seria, resultColumns, currentOperator);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentQuery.list) {
|
||||||
|
currentQuery.list.forEach((listItem) => {
|
||||||
|
fillDataFromList(listItem, resultColumns);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const rowsLength = resultColumns.length > 0 ? resultColumns[0].data.length : 0;
|
const rowsLength = resultColumns.length > 0 ? resultColumns[0].data.length : 0;
|
||||||
|
@ -16,7 +16,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|||||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import { generatePath } from 'react-router-dom';
|
import { generatePath } from 'react-router-dom';
|
||||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
@ -34,19 +34,38 @@ function TracesExplorer(): JSX.Element {
|
|||||||
redirectWithQueryBuilderData,
|
redirectWithQueryBuilderData,
|
||||||
} = useQueryBuilder();
|
} = useQueryBuilder();
|
||||||
|
|
||||||
const tabsItems = getTabsItems();
|
const currentTab = panelType || PANEL_TYPES.LIST;
|
||||||
const currentTab = panelType || PANEL_TYPES.TIME_SERIES;
|
|
||||||
|
const isMultipleQueries = useMemo(
|
||||||
|
() =>
|
||||||
|
currentQuery.builder.queryData.length > 1 ||
|
||||||
|
currentQuery.builder.queryFormulas.length > 0,
|
||||||
|
[currentQuery],
|
||||||
|
);
|
||||||
|
|
||||||
const defaultQuery = useMemo(
|
const defaultQuery = useMemo(
|
||||||
() =>
|
() =>
|
||||||
updateAllQueriesOperators(
|
updateAllQueriesOperators(
|
||||||
initialQueriesMap.traces,
|
initialQueriesMap.traces,
|
||||||
PANEL_TYPES.TIME_SERIES,
|
PANEL_TYPES.LIST,
|
||||||
DataSource.TRACES,
|
DataSource.TRACES,
|
||||||
),
|
),
|
||||||
[updateAllQueriesOperators],
|
[updateAllQueriesOperators],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isGroupByExist = useMemo(() => {
|
||||||
|
const groupByCount: number = currentQuery.builder.queryData.reduce<number>(
|
||||||
|
(acc, query) => acc + query.groupBy.length,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return groupByCount > 0;
|
||||||
|
}, [currentQuery]);
|
||||||
|
|
||||||
|
const tabsItems = getTabsItems({
|
||||||
|
isListViewDisabled: isMultipleQueries || isGroupByExist,
|
||||||
|
});
|
||||||
|
|
||||||
const exportDefaultQuery = useMemo(
|
const exportDefaultQuery = useMemo(
|
||||||
() =>
|
() =>
|
||||||
updateAllQueriesOperators(
|
updateAllQueriesOperators(
|
||||||
@ -114,6 +133,17 @@ function TracesExplorer(): JSX.Element {
|
|||||||
|
|
||||||
useShareBuilderUrl(defaultQuery);
|
useShareBuilderUrl(defaultQuery);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const shouldChangeView = isMultipleQueries || isGroupByExist;
|
||||||
|
|
||||||
|
if (
|
||||||
|
(currentTab === PANEL_TYPES.LIST || currentTab === PANEL_TYPES.TRACE) &&
|
||||||
|
shouldChangeView
|
||||||
|
) {
|
||||||
|
handleTabChange(PANEL_TYPES.TIME_SERIES);
|
||||||
|
}
|
||||||
|
}, [currentTab, isMultipleQueries, isGroupByExist, handleTabChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<QuerySection />
|
<QuerySection />
|
||||||
|
@ -1,17 +1,25 @@
|
|||||||
import { TabsProps } from 'antd';
|
import { TabsProps } from 'antd';
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
import TimeSeriesView from 'container/TimeSeriesView';
|
import TimeSeriesView from 'container/TimeSeriesView';
|
||||||
|
import TracesView from 'container/TracesExplorer/TracesView';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
export const getTabsItems = (): TabsProps['items'] => [
|
interface GetTabsItemsProps {
|
||||||
|
isListViewDisabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTabsItems = ({
|
||||||
|
isListViewDisabled,
|
||||||
|
}: GetTabsItemsProps): TabsProps['items'] => [
|
||||||
|
{
|
||||||
|
label: 'Traces',
|
||||||
|
key: PANEL_TYPES.TRACE,
|
||||||
|
children: <TracesView />,
|
||||||
|
disabled: isListViewDisabled,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Time Series',
|
label: 'Time Series',
|
||||||
key: PANEL_TYPES.TIME_SERIES,
|
key: PANEL_TYPES.TIME_SERIES,
|
||||||
children: <TimeSeriesView dataSource={DataSource.TRACES} />,
|
children: <TimeSeriesView dataSource={DataSource.TRACES} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Traces',
|
|
||||||
key: PANEL_TYPES.TRACE,
|
|
||||||
children: <div>Traces tab</div>,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
@ -16,12 +16,14 @@ import { SuccessResponse } from 'types/api';
|
|||||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
import { Pagination } from 'hooks/queryPagination';
|
||||||
|
|
||||||
export async function GetMetricQueryRange({
|
export async function GetMetricQueryRange({
|
||||||
query,
|
query,
|
||||||
globalSelectedInterval,
|
globalSelectedInterval,
|
||||||
graphType,
|
graphType,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
|
tableParams,
|
||||||
variables = {},
|
variables = {},
|
||||||
params = {},
|
params = {},
|
||||||
}: GetQueryResultsProps): Promise<SuccessResponse<MetricRangePayloadProps>> {
|
}: GetQueryResultsProps): Promise<SuccessResponse<MetricRangePayloadProps>> {
|
||||||
@ -38,8 +40,9 @@ export async function GetMetricQueryRange({
|
|||||||
switch (query.queryType) {
|
switch (query.queryType) {
|
||||||
case EQueryType.QUERY_BUILDER: {
|
case EQueryType.QUERY_BUILDER: {
|
||||||
const { queryData: data, queryFormulas } = query.builder;
|
const { queryData: data, queryFormulas } = query.builder;
|
||||||
const currentQueryData = mapQueryDataToApi(data, 'queryName');
|
const currentQueryData = mapQueryDataToApi(data, 'queryName', tableParams);
|
||||||
const currentFormulas = mapQueryDataToApi(queryFormulas, 'queryName');
|
const currentFormulas = mapQueryDataToApi(queryFormulas, 'queryName');
|
||||||
|
|
||||||
const builderQueries = {
|
const builderQueries = {
|
||||||
...currentQueryData.data,
|
...currentQueryData.data,
|
||||||
...currentFormulas.data,
|
...currentFormulas.data,
|
||||||
@ -140,4 +143,8 @@ export interface GetQueryResultsProps {
|
|||||||
globalSelectedInterval: Time;
|
globalSelectedInterval: Time;
|
||||||
variables?: Record<string, unknown>;
|
variables?: Record<string, unknown>;
|
||||||
params?: Record<string, unknown>;
|
params?: Record<string, unknown>;
|
||||||
|
tableParams?: {
|
||||||
|
pagination?: Pagination;
|
||||||
|
selectColumns?: any;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ export interface PayloadProps {
|
|||||||
result: QueryData[];
|
result: QueryData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListItem = { timestamp: string; data: Omit<ILog, 'timestamp'> };
|
export type ListItem = { timestamp: string; data: Omit<ILog, 'timestamp'> };
|
||||||
|
|
||||||
export interface QueryData {
|
export interface QueryData {
|
||||||
metric: {
|
metric: {
|
||||||
|
@ -18,6 +18,7 @@ export type HandleChangeQueryData = <
|
|||||||
export type UseQueryOperations = (
|
export type UseQueryOperations = (
|
||||||
params: UseQueryOperationsParams,
|
params: UseQueryOperationsParams,
|
||||||
) => {
|
) => {
|
||||||
|
isTracePanelType: boolean;
|
||||||
isMetricsDataSource: boolean;
|
isMetricsDataSource: boolean;
|
||||||
operators: SelectOption<string, string>[];
|
operators: SelectOption<string, string>[];
|
||||||
listOfAdditionalFilters: string[];
|
listOfAdditionalFilters: string[];
|
||||||
|
@ -143,8 +143,8 @@ export type PanelTypeKeys =
|
|||||||
| 'VALUE'
|
| 'VALUE'
|
||||||
| 'TABLE'
|
| 'TABLE'
|
||||||
| 'LIST'
|
| 'LIST'
|
||||||
| 'EMPTY_WIDGET'
|
| 'TRACE'
|
||||||
| 'TRACE';
|
| 'EMPTY_WIDGET';
|
||||||
|
|
||||||
export type ReduceOperators = 'last' | 'sum' | 'avg' | 'max' | 'min';
|
export type ReduceOperators = 'last' | 'sum' | 'avg' | 'max' | 'min';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user