feat: added global search on table panel (#5893)

* feat: added global search on table panel

* feat: added global search on table panel

* feat: added global search conditionally and with new design

* feat: removed state from datasource

* feat: added global search in full view

* feat: added lightMode styles

* feat: added test cases for querytable and widgetHeader - global search
This commit is contained in:
SagarRajput-7 2024-09-20 16:36:35 +05:30 committed by GitHub
parent ced72f86a4
commit cb1cd3555b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1055 additions and 46 deletions

View File

@ -15,6 +15,13 @@
box-sizing: border-box;
margin: 16px 0;
border-radius: 3px;
.global-search {
.ant-input-group-addon {
border: none;
background-color: var(--bg-ink-300);
}
}
}
.height-widget {
@ -55,3 +62,15 @@
}
}
}
.lightMode {
.full-view-container {
.graph-container {
.global-search {
.ant-input-group-addon {
background-color: var(--bg-vanilla-200);
}
}
}
}
}

View File

@ -1,7 +1,11 @@
import './WidgetFullView.styles.scss';
import { LoadingOutlined, SyncOutlined } from '@ant-design/icons';
import { Button, Spin } from 'antd';
import {
LoadingOutlined,
SearchOutlined,
SyncOutlined,
} from '@ant-design/icons';
import { Button, Input, Spin } from 'antd';
import cx from 'classnames';
import { ToggleGraphProps } from 'components/Graph/types';
import Spinner from 'components/Spinner';
@ -172,6 +176,10 @@ function FullView({
const isListView = widget.panelTypes === PANEL_TYPES.LIST;
const isTablePanel = widget.panelTypes === PANEL_TYPES.TABLE;
const [searchTerm, setSearchTerm] = useState<string>('');
if (response.isLoading && widget.panelTypes !== PANEL_TYPES.LIST) {
return <Spinner height="100%" size="large" tip="Loading..." />;
}
@ -216,6 +224,18 @@ function FullView({
}}
isGraphLegendToggleAvailable={canModifyChart}
>
{isTablePanel && (
<Input
addonBefore={<SearchOutlined size={14} />}
className="global-search"
placeholder="Search..."
allowClear
key={widget.id}
onChange={(e): void => {
setSearchTerm(e.target.value || '');
}}
/>
)}
<PanelWrapper
queryResponse={response}
widget={widget}
@ -226,6 +246,7 @@ function FullView({
graphVisibility={graphsVisibilityStates}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
searchTerm={searchTerm}
/>
</GraphContainer>
</div>

View File

@ -234,6 +234,8 @@ function WidgetGraphComponent({
});
};
const [searchTerm, setSearchTerm] = useState<string>('');
const loadingState =
(queryResponse.isLoading || queryResponse.status === 'idle') &&
widget.panelTypes !== PANEL_TYPES.LIST;
@ -317,6 +319,7 @@ function WidgetGraphComponent({
isWarning={isWarning}
isFetchingResponse={isFetchingResponse}
tableProcessedDataRef={tableProcessedDataRef}
setSearchTerm={setSearchTerm}
/>
</div>
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
@ -337,6 +340,7 @@ function WidgetGraphComponent({
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
searchTerm={searchTerm}
/>
</div>
)}

View File

@ -2,7 +2,7 @@
display: flex;
justify-content: space-between;
align-items: center;
height: 30px;
height: 36px;
width: 100%;
padding: 0.5rem;
box-sizing: border-box;
@ -10,6 +10,14 @@
font-weight: 600;
cursor: move;
.ant-input-group-addon {
border: none;
background-color: var(--bg-ink-500);
}
.search-header-icons {
cursor: pointer;
}
}
.widget-header-title {
@ -19,6 +27,7 @@
.widget-header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.widget-header-more-options {
visibility: hidden;
@ -30,6 +39,10 @@
padding: 8px;
}
.widget-header-more-options-visible {
visibility: visible;
}
.widget-header-hover {
visibility: visible;
}
@ -37,3 +50,11 @@
.widget-api-actions {
padding-right: 0.25rem;
}
.lightMode {
.widget-header-container {
.ant-input-group-addon {
background-color: inherit;
}
}
}

View File

@ -9,9 +9,10 @@ import {
ExclamationCircleOutlined,
FullscreenOutlined,
MoreOutlined,
SearchOutlined,
WarningOutlined,
} from '@ant-design/icons';
import { Dropdown, MenuProps, Tooltip, Typography } from 'antd';
import { Dropdown, Input, MenuProps, Tooltip, Typography } from 'antd';
import Spinner from 'components/Spinner';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
@ -20,8 +21,9 @@ import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { isEmpty } from 'lodash-es';
import { X } from 'lucide-react';
import { unparse } from 'papaparse';
import { ReactNode, useCallback, useMemo } from 'react';
import { ReactNode, useCallback, useMemo, useState } from 'react';
import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
@ -51,6 +53,7 @@ interface IWidgetHeaderProps {
isWarning: boolean;
isFetchingResponse: boolean;
tableProcessedDataRef: React.MutableRefObject<RowData[]>;
setSearchTerm: React.Dispatch<React.SetStateAction<string>>;
}
function WidgetHeader({
@ -67,6 +70,7 @@ function WidgetHeader({
isWarning,
isFetchingResponse,
tableProcessedDataRef,
setSearchTerm,
}: IWidgetHeaderProps): JSX.Element | null {
const onEditHandler = useCallback((): void => {
const widgetId = widget.id;
@ -187,6 +191,10 @@ function WidgetHeader({
const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);
const [showGlobalSearch, setShowGlobalSearch] = useState(false);
const globalSearchAvailable = widget.panelTypes === PANEL_TYPES.TABLE;
const menu = useMemo(
() => ({
items: updatedMenuList,
@ -201,6 +209,31 @@ function WidgetHeader({
return (
<div className="widget-header-container">
{showGlobalSearch ? (
<Input
addonBefore={<SearchOutlined size={14} />}
placeholder="Search..."
bordered={false}
data-testid="widget-header-search-input"
autoFocus
addonAfter={
<X
size={14}
onClick={(e): void => {
e.stopPropagation();
e.preventDefault();
setShowGlobalSearch(false);
}}
className="search-header-icons"
/>
}
key={widget.id}
onChange={(e): void => {
setSearchTerm(e.target.value || '');
}}
/>
) : (
<>
<Typography.Text
ellipsis
data-testid={title}
@ -232,15 +265,24 @@ function WidgetHeader({
<WarningOutlined />
</Tooltip>
)}
{globalSearchAvailable && (
<SearchOutlined
className="search-header-icons"
onClick={(): void => setShowGlobalSearch(true)}
data-testid="widget-header-search"
/>
)}
<Dropdown menu={menu} trigger={['hover']} placement="bottomRight">
<MoreOutlined
data-testid="widget-header-options"
className={`widget-header-more-options ${
parentHover ? 'widget-header-hover' : ''
}`}
} ${globalSearchAvailable ? 'widget-header-more-options-visible' : ''}`}
/>
</Dropdown>
</div>
</>
)}
</div>
);
}

View File

@ -14,6 +14,7 @@ export type GridTableComponentProps = {
columnUnits?: ColumnUnit;
tableProcessedDataRef?: React.MutableRefObject<RowData[]>;
sticky?: TableProps<RowData>['sticky'];
searchTerm?: string;
} & Pick<LogsExplorerTableProps, 'data'> &
Omit<TableProps<RowData>, 'columns' | 'dataSource'>;

View File

@ -16,6 +16,7 @@ function PanelWrapper({
selectedGraph,
tableProcessedDataRef,
customTooltipElement,
searchTerm,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@ -39,6 +40,7 @@ function PanelWrapper({
selectedGraph={selectedGraph}
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
searchTerm={searchTerm}
/>
);
}

View File

@ -8,6 +8,7 @@ function TablePanelWrapper({
widget,
queryResponse,
tableProcessedDataRef,
searchTerm,
}: PanelWrapperProps): JSX.Element {
const panelData =
(queryResponse.data?.payload?.data?.result?.[0] as any)?.table || [];
@ -20,6 +21,7 @@ function TablePanelWrapper({
columnUnits={widget.columnUnits}
tableProcessedDataRef={tableProcessedDataRef}
sticky={widget.panelTypes === PANEL_TYPES.TABLE}
searchTerm={searchTerm}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}
/>

View File

@ -23,6 +23,7 @@ export type PanelWrapperProps = {
onDragSelect: (start: number, end: number) => void;
selectedGraph?: PANEL_TYPES;
tableProcessedDataRef?: React.MutableRefObject<RowData[]>;
searchTerm?: string;
customTooltipElement?: HTMLDivElement;
};

View File

@ -19,4 +19,5 @@ export type QueryTableProps = Omit<
columns?: ColumnsType<RowData>;
dataSource?: RowData[];
sticky?: TableProps<RowData>['sticky'];
searchTerm?: string;
};

View File

@ -3,8 +3,11 @@ import './QueryTable.styles.scss';
import { ResizeTable } from 'components/ResizeTable';
import Download from 'container/Download/Download';
import { IServiceName } from 'container/MetricsApplication/Tabs/types';
import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
import { useMemo } from 'react';
import {
createTableColumnsFromQuery,
RowData,
} from 'lib/query/createTableColumnsFromQuery';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { QueryTableProps } from './QueryTable.intefaces';
@ -20,6 +23,7 @@ export function QueryTable({
columns,
dataSource,
sticky,
searchTerm,
...props
}: QueryTableProps): JSX.Element {
const { isDownloadEnabled = false, fileName = '' } = downloadOption || {};
@ -55,6 +59,27 @@ export function QueryTable({
hideOnSinglePage: true,
};
const [filterTable, setFilterTable] = useState<RowData[] | null>(null);
const onTableSearch = useCallback(
(value?: string): void => {
const filterTable = newDataSource.filter((o) =>
Object.keys(o).some((k) =>
String(o[k])
.toLowerCase()
.includes(value?.toLowerCase() || ''),
),
);
setFilterTable(filterTable);
},
[newDataSource],
);
useEffect(() => {
onTableSearch(searchTerm);
}, [newDataSource, onTableSearch, searchTerm]);
return (
<div className="query-table">
{isDownloadEnabled && (
@ -69,7 +94,7 @@ export function QueryTable({
<ResizeTable
columns={tableColumns}
tableLayout="fixed"
dataSource={newDataSource}
dataSource={filterTable === null ? newDataSource : filterTable}
scroll={{ x: true }}
pagination={paginationConfig}
sticky={sticky}

View File

@ -0,0 +1,73 @@
/* eslint-disable react/jsx-props-no-spreading */
import WidgetHeader from 'container/GridCardLayout/WidgetHeader';
import { fireEvent, render } from 'tests/test-utils';
import { QueryTable } from '../QueryTable';
import { QueryTableProps, WidgetHeaderProps } from './mocks';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: (): { pathname: string } => ({
pathname: ``,
}),
}));
// Mock useDashabord hook
jest.mock('providers/Dashboard/Dashboard', () => ({
useDashboard: (): any => ({
selectedDashboard: {
data: {
variables: [],
},
},
}),
}));
describe('QueryTable -', () => {
it('should render correctly with all the data rows', () => {
const { container } = render(<QueryTable {...QueryTableProps} />);
const tableRows = container.querySelectorAll('tr.ant-table-row');
expect(tableRows.length).toBe(QueryTableProps.queryTableData.rows.length);
});
it('should render correctly with searchTerm', () => {
const { container } = render(
<QueryTable {...QueryTableProps} searchTerm="frontend" />,
);
const tableRows = container.querySelectorAll('tr.ant-table-row');
expect(tableRows.length).toBe(3);
});
});
const setSearchTerm = jest.fn();
describe('WidgetHeader -', () => {
it('global search option should be working', () => {
const { getByText, getByTestId } = render(
<WidgetHeader {...WidgetHeaderProps} setSearchTerm={setSearchTerm} />,
);
expect(getByText('Table - Panel')).toBeInTheDocument();
const searchWidget = getByTestId('widget-header-search');
expect(searchWidget).toBeInTheDocument();
// click and open the search input
fireEvent.click(searchWidget);
// check if input is opened
const searchInput = getByTestId('widget-header-search-input');
expect(searchInput).toBeInTheDocument();
// enter search term
fireEvent.change(searchInput, { target: { value: 'frontend' } });
// check if search term is set
expect(setSearchTerm).toHaveBeenCalledWith('frontend');
expect(searchInput).toHaveValue('frontend');
});
it('global search should not be present for non-table panel', () => {
const { queryByTestId } = render(
<WidgetHeader
{...WidgetHeaderProps}
widget={{ ...WidgetHeaderProps.widget, panelTypes: 'chart' }}
/>,
);
expect(queryByTestId('widget-header-search')).not.toBeInTheDocument();
});
});

View File

@ -0,0 +1,797 @@
/* eslint-disable sonarjs/no-duplicate-string */
export const QueryTableProps: any = {
props: {
loading: false,
size: 'small',
},
queryTableData: {
columns: [
{
name: 'resource_host_name',
queryName: '',
isValueColumn: false,
},
{
name: 'service_name',
queryName: '',
isValueColumn: false,
},
{
name: 'operation',
queryName: '',
isValueColumn: false,
},
{
name: 'A',
queryName: 'A',
isValueColumn: true,
},
],
rows: [
{
data: {
A: 11.5,
operation: 'GetDriver',
resource_host_name: 'test-hs-name',
service_name: 'redis',
},
},
{
data: {
A: 10.13,
operation: 'HTTP GET',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
},
{
data: {
A: 9.21,
operation: 'HTTP GET /route',
resource_host_name: 'test-hs-name',
service_name: 'route',
},
},
{
data: {
A: 9.21,
operation: 'HTTP GET: /route',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
},
{
data: {
A: 0.92,
operation: 'HTTP GET: /customer',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
},
{
data: {
A: 0.92,
operation: 'SQL SELECT',
resource_host_name: 'test-hs-name',
service_name: 'mysql',
},
},
{
data: {
A: 0.92,
operation: 'HTTP GET /customer',
resource_host_name: 'test-hs-name',
service_name: 'customer',
},
},
],
},
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: 'float64',
id: 'signoz_calls_total--float64--Sum--true',
isColumn: true,
isJSON: false,
key: 'signoz_calls_total',
type: 'Sum',
},
aggregateOperator: 'rate',
dataSource: 'metrics',
disabled: false,
expression: 'A',
filters: {
items: [],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: 'string',
id: 'resource_host_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'resource_host_name',
type: 'tag',
},
{
dataType: 'string',
id: 'service_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'service_name',
type: 'tag',
},
{
dataType: 'string',
id: 'operation--string--tag--false',
isColumn: false,
isJSON: false,
key: 'operation',
type: 'tag',
},
],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '1e08128f-c6a3-42ff-8033-4e38d291cf0a',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: 'builder',
},
columns: [
{
dataIndex: 'resource_host_name',
title: 'resource_host_name',
width: 145,
},
{
dataIndex: 'service_name',
title: 'service_name',
width: 145,
},
{
dataIndex: 'operation',
title: 'operation',
width: 145,
},
{
dataIndex: 'A',
title: 'A',
width: 145,
},
],
dataSource: [
{
A: 11.5,
operation: 'GetDriver',
resource_host_name: 'test-hs-name',
service_name: 'redis',
},
{
A: 10.13,
operation: 'HTTP GET',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
{
A: 9.21,
operation: 'HTTP GET /route',
resource_host_name: 'test-hs-name',
service_name: 'route',
},
{
A: 9.21,
operation: 'HTTP GET: /route',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
{
A: 0.92,
operation: 'HTTP GET: /customer',
resource_host_name: 'test-hs-name',
service_name: 'frontend',
},
{
A: 0.92,
operation: 'SQL SELECT',
resource_host_name: 'test-hs-name',
service_name: 'mysql',
},
{
A: 0.92,
operation: 'HTTP GET /customer',
resource_host_name: 'test-hs-name',
service_name: 'customer',
},
],
sticky: true,
searchTerm: '',
};
export const WidgetHeaderProps: any = {
title: 'Table - Panel',
widget: {
bucketCount: 30,
bucketWidth: 0,
columnUnits: {},
description: '',
fillSpans: false,
id: 'add65f0d-7662-4024-af51-da567759235d',
isStacked: false,
mergeAllActiveQueries: false,
nullZeroValues: 'zero',
opacity: '1',
panelTypes: 'table',
query: {
builder: {
queryData: [
{
aggregateAttribute: {
dataType: 'float64',
id: 'signoz_calls_total--float64--Sum--true',
isColumn: true,
isJSON: false,
key: 'signoz_calls_total',
type: 'Sum',
},
aggregateOperator: 'rate',
dataSource: 'metrics',
disabled: false,
expression: 'A',
filters: {
items: [],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: 'string',
id: 'resource_host_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'resource_host_name',
type: 'tag',
},
{
dataType: 'string',
id: 'service_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'service_name',
type: 'tag',
},
{
dataType: 'string',
id: 'operation--string--tag--false',
isColumn: false,
isJSON: false,
key: 'operation',
type: 'tag',
},
],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
],
queryFormulas: [],
},
clickhouse_sql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
id: '1e08128f-c6a3-42ff-8033-4e38d291cf0a',
promql: [
{
disabled: false,
legend: '',
name: 'A',
query: '',
},
],
queryType: 'builder',
},
selectedLogFields: [
{
dataType: 'string',
name: 'body',
type: '',
},
{
dataType: 'string',
name: 'timestamp',
type: '',
},
],
selectedTracesFields: [
{
dataType: 'string',
id: 'serviceName--string--tag--true',
isColumn: true,
isJSON: false,
key: 'serviceName',
type: 'tag',
},
{
dataType: 'string',
id: 'name--string--tag--true',
isColumn: true,
isJSON: false,
key: 'name',
type: 'tag',
},
{
dataType: 'float64',
id: 'durationNano--float64--tag--true',
isColumn: true,
isJSON: false,
key: 'durationNano',
type: 'tag',
},
{
dataType: 'string',
id: 'httpMethod--string--tag--true',
isColumn: true,
isJSON: false,
key: 'httpMethod',
type: 'tag',
},
{
dataType: 'string',
id: 'responseStatusCode--string--tag--true',
isColumn: true,
isJSON: false,
key: 'responseStatusCode',
type: 'tag',
},
],
softMax: 0,
softMin: 0,
stackedBarChart: false,
thresholds: [],
timePreferance: 'GLOBAL_TIME',
title: 'Table - Panel',
yAxisUnit: 'none',
},
parentHover: false,
queryResponse: {
status: 'success',
isLoading: false,
isSuccess: true,
isError: false,
isIdle: false,
data: {
statusCode: 200,
error: null,
message: 'success',
payload: {
status: 'success',
data: {
resultType: '',
result: [
{
table: {
columns: [
{
name: 'resource_host_name',
queryName: '',
isValueColumn: false,
},
{
name: 'service_name',
queryName: '',
isValueColumn: false,
},
{
name: 'operation',
queryName: '',
isValueColumn: false,
},
{
name: 'A',
queryName: 'A',
isValueColumn: true,
},
],
rows: [
{
data: {
A: 11.67,
operation: 'GetDriver',
resource_host_name: '4f6ec470feea',
service_name: 'redis',
},
},
{
data: {
A: 10.26,
operation: 'HTTP GET',
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
},
},
{
data: {
A: 9.33,
operation: 'HTTP GET: /route',
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
},
},
{
data: {
A: 9.33,
operation: 'HTTP GET /route',
resource_host_name: '4f6ec470feea',
service_name: 'route',
},
},
{
data: {
A: 0.93,
operation: 'FindDriverIDs',
resource_host_name: '4f6ec470feea',
service_name: 'redis',
},
},
{
data: {
A: 0.93,
operation: 'HTTP GET: /customer',
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
},
},
{
data: {
A: 0.93,
operation: '/driver.DriverService/FindNearest',
resource_host_name: '4f6ec470feea',
service_name: 'driver',
},
},
{
data: {
A: 0.93,
operation: '/driver.DriverService/FindNearest',
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
},
},
{
data: {
A: 0.93,
operation: 'SQL SELECT',
resource_host_name: '4f6ec470feea',
service_name: 'mysql',
},
},
{
data: {
A: 0.93,
operation: 'HTTP GET /customer',
resource_host_name: '4f6ec470feea',
service_name: 'customer',
},
},
{
data: {
A: 0.93,
operation: 'HTTP GET /dispatch',
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
},
},
{
data: {
A: 0.21,
operation: 'check_request limit',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.21,
operation: 'authenticate_check_cache',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.21,
operation: 'authenticate_check_db',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.21,
operation: 'authenticate',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.21,
operation: 'check cart in cache',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.2,
operation: 'get_cart',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.2,
operation: 'check cart in db',
resource_host_name: '',
service_name: 'demo-app',
},
},
{
data: {
A: 0.2,
operation: 'home',
resource_host_name: '',
service_name: 'demo-app',
},
},
],
},
},
],
},
},
params: {
start: 1726669030000,
end: 1726670830000,
step: 60,
variables: {},
formatForWeb: true,
compositeQuery: {
queryType: 'builder',
panelType: 'table',
fillGaps: false,
builderQueries: {
A: {
aggregateAttribute: {
dataType: 'float64',
id: 'signoz_calls_total--float64--Sum--true',
isColumn: true,
isJSON: false,
key: 'signoz_calls_total',
type: 'Sum',
},
aggregateOperator: 'rate',
dataSource: 'metrics',
disabled: false,
expression: 'A',
filters: {
items: [],
op: 'AND',
},
functions: [],
groupBy: [
{
dataType: 'string',
id: 'resource_host_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'resource_host_name',
type: 'tag',
},
{
dataType: 'string',
id: 'service_name--string--tag--false',
isColumn: false,
isJSON: false,
key: 'service_name',
type: 'tag',
},
{
dataType: 'string',
id: 'operation--string--tag--false',
isColumn: false,
isJSON: false,
key: 'operation',
type: 'tag',
},
],
having: [],
legend: '',
limit: null,
orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
timeAggregation: 'rate',
},
},
},
},
},
dataUpdatedAt: 1726670830710,
error: null,
errorUpdatedAt: 0,
failureCount: 0,
errorUpdateCount: 0,
isFetched: true,
isFetchedAfterMount: true,
isFetching: false,
isRefetching: false,
isLoadingError: false,
isPlaceholderData: false,
isPreviousData: false,
isRefetchError: false,
isStale: true,
},
headerMenuList: ['view', 'clone', 'delete', 'edit'],
isWarning: false,
isFetchingResponse: false,
tableProcessedDataRef: {
current: [
{
resource_host_name: '4f6ec470feea',
service_name: 'redis',
operation: 'GetDriver',
A: 11.67,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
operation: 'HTTP GET',
A: 10.26,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
operation: 'HTTP GET: /route',
A: 9.33,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'route',
operation: 'HTTP GET /route',
A: 9.33,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'redis',
operation: 'FindDriverIDs',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
operation: 'HTTP GET: /customer',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'driver',
operation: '/driver.DriverService/FindNearest',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
operation: '/driver.DriverService/FindNearest',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'mysql',
operation: 'SQL SELECT',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'customer',
operation: 'HTTP GET /customer',
A: 0.93,
},
{
resource_host_name: '4f6ec470feea',
service_name: 'frontend',
operation: 'HTTP GET /dispatch',
A: 0.93,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'check_request limit',
A: 0.21,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'authenticate_check_cache',
A: 0.21,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'authenticate_check_db',
A: 0.21,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'authenticate',
A: 0.21,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'check cart in cache',
A: 0.21,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'get_cart',
A: 0.2,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'check cart in db',
A: 0.2,
},
{
resource_host_name: '',
service_name: 'demo-app',
operation: 'home',
A: 0.2,
},
],
},
};