mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 05:59:01 +08:00
feat: new dep services top 10 errors localised date picker agrregate domain details etc
This commit is contained in:
parent
8b30e3cc5c
commit
1123a9a93d
@ -54,6 +54,7 @@ export const REACT_QUERY_KEY = {
|
||||
|
||||
// API Monitoring Query Keys
|
||||
GET_DOMAINS_LIST: 'GET_DOMAINS_LIST',
|
||||
GET_DOMAIN_METRICS_DATA: 'GET_DOMAIN_METRICS_DATA',
|
||||
GET_ENDPOINTS_LIST_BY_DOMAIN: 'GET_ENDPOINTS_LIST_BY_DOMAIN',
|
||||
GET_TOP_ERRORS_BY_DOMAIN: 'GET_TOP_ERRORS_BY_DOMAIN',
|
||||
GET_NESTED_ENDPOINTS_LIST: 'GET_NESTED_ENDPOINTS_LIST',
|
||||
|
@ -159,7 +159,7 @@ function AllEndPoints({
|
||||
const handleRowClick = (record: EndPointsTableRowData): void => {
|
||||
if (groupBy.length === 0) {
|
||||
setSelectedEndPointName(record.endpointName); // this will open up the endpoint details tab
|
||||
setSelectedView(VIEW_TYPES.ENDPOINT_DETAILS);
|
||||
setSelectedView(VIEW_TYPES.ENDPOINT_STATS);
|
||||
logEvent('API Monitoring: Endpoint name row clicked', {});
|
||||
} else {
|
||||
handleGroupByRowClick(record); // this will prepare the nested query payload
|
||||
|
@ -252,6 +252,9 @@
|
||||
border: 1px solid var(--bg-slate-500);
|
||||
|
||||
.endpoints-table-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
color: var(--Vanilla-100, #fff);
|
||||
font-family: Inter;
|
||||
@ -392,6 +395,21 @@
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.top-errors-dropdown-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
|
||||
.endpoint-details-filters-container-dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.endpoint-details-filters-container-search {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.endpoint-details-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -20,7 +20,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import AllEndPoints from './AllEndPoints';
|
||||
import DomainMetrics from './components/DomainMetrics';
|
||||
import { VIEW_TYPES, VIEWS } from './constants';
|
||||
import EndPointDetailsWrapper from './EndPointDetailsWrapper';
|
||||
import EndPointDetails from './EndPointDetails';
|
||||
import TopErrors from './TopErrors';
|
||||
|
||||
const TimeRangeOffset = 1000000000;
|
||||
@ -156,7 +156,10 @@ function DomainDetails({
|
||||
>
|
||||
{domainData && (
|
||||
<>
|
||||
<DomainMetrics domainData={domainData} />
|
||||
<DomainMetrics
|
||||
domainName={domainData.domainName}
|
||||
timeRange={modalTimeRange}
|
||||
/>
|
||||
<div className="views-tabs-container">
|
||||
<Radio.Group
|
||||
className="views-tabs"
|
||||
@ -174,13 +177,13 @@ function DomainDetails({
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
className={
|
||||
selectedView === VIEW_TYPES.ENDPOINT_DETAILS
|
||||
selectedView === VIEW_TYPES.ENDPOINT_STATS
|
||||
? 'tab selected_view'
|
||||
: 'tab'
|
||||
}
|
||||
value={VIEW_TYPES.ENDPOINT_DETAILS}
|
||||
value={VIEW_TYPES.ENDPOINT_STATS}
|
||||
>
|
||||
<div className="view-title">Endpoint Details</div>
|
||||
<div className="view-title">Endpoint(s) Stats</div>
|
||||
</Radio.Button>
|
||||
<Radio.Button
|
||||
className={
|
||||
@ -188,7 +191,7 @@ function DomainDetails({
|
||||
}
|
||||
value={VIEW_TYPES.TOP_ERRORS}
|
||||
>
|
||||
<div className="view-title">Top Errors</div>
|
||||
<div className="view-title">Top 10 Errors</div>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
@ -203,13 +206,14 @@ function DomainDetails({
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedView === VIEW_TYPES.ENDPOINT_DETAILS && (
|
||||
<EndPointDetailsWrapper
|
||||
{selectedView === VIEW_TYPES.ENDPOINT_STATS && (
|
||||
<EndPointDetails
|
||||
domainName={domainData.domainName}
|
||||
endPointName={selectedEndPointName}
|
||||
setSelectedEndPointName={setSelectedEndPointName}
|
||||
domainListFilters={domainListFilters}
|
||||
timeRange={modalTimeRange}
|
||||
// handleTimeChange={handleTimeChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -8,6 +8,10 @@ import {
|
||||
getRateOverTimeWidgetData,
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
|
||||
// import {
|
||||
// CustomTimeType,
|
||||
// Time,
|
||||
// } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
@ -29,7 +33,8 @@ function EndPointDetails({
|
||||
setSelectedEndPointName,
|
||||
domainListFilters,
|
||||
timeRange,
|
||||
}: {
|
||||
}: // handleTimeChange,
|
||||
{
|
||||
domainName: string;
|
||||
endPointName: string;
|
||||
setSelectedEndPointName: (value: string) => void;
|
||||
@ -38,6 +43,10 @@ function EndPointDetails({
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
};
|
||||
// handleTimeChange: (
|
||||
// interval: Time | CustomTimeType,
|
||||
// dateTimeRange?: [number, number],
|
||||
// ) => void;
|
||||
}): JSX.Element {
|
||||
const { startTime: minTime, endTime: maxTime } = timeRange;
|
||||
|
||||
@ -47,6 +56,7 @@ function EndPointDetails({
|
||||
op: 'AND',
|
||||
items: [],
|
||||
});
|
||||
// [TODO] if endPointName is there then add it to the filters under http.url key
|
||||
|
||||
// Manually update the query to include the filters
|
||||
// Because using the hook is causing the global domain
|
||||
@ -78,15 +88,8 @@ function EndPointDetails({
|
||||
);
|
||||
|
||||
const endPointDetailsQueryPayload = useMemo(
|
||||
() =>
|
||||
getEndPointDetailsQueryPayload(
|
||||
domainName,
|
||||
endPointName,
|
||||
minTime,
|
||||
maxTime,
|
||||
filters,
|
||||
),
|
||||
[domainName, endPointName, filters, minTime, maxTime],
|
||||
() => getEndPointDetailsQueryPayload(domainName, minTime, maxTime, filters),
|
||||
[domainName, filters, minTime, maxTime],
|
||||
);
|
||||
|
||||
const endPointDetailsDataQueries = useQueries(
|
||||
@ -141,6 +144,20 @@ function EndPointDetails({
|
||||
[domainName, endPointName, filters, domainListFilters],
|
||||
);
|
||||
|
||||
// // [TODO] Fix this later
|
||||
// const onDragSelect = useCallback(
|
||||
// (start: number, end: number) => {
|
||||
// const startTimestamp = Math.trunc(start);
|
||||
// const endTimestamp = Math.trunc(end);
|
||||
|
||||
// if (startTimestamp !== endTimestamp) {
|
||||
// // update the value in local time picker
|
||||
// handleTimeChange('custom', [startTimestamp, endTimestamp]);
|
||||
// }
|
||||
// },
|
||||
// [handleTimeChange],
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="endpoint-details-container">
|
||||
<div className="endpoint-details-filters-container">
|
||||
@ -166,7 +183,9 @@ function EndPointDetails({
|
||||
<div className="endpoint-meta-data">
|
||||
<div className="endpoint-meta-data-pill">
|
||||
<div className="endpoint-meta-data-label">Endpoint</div>
|
||||
<div className="endpoint-meta-data-value">{endpoint || '-'}</div>
|
||||
<div className="endpoint-meta-data-value">
|
||||
{endpoint || 'All Endpoints'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="endpoint-meta-data-pill">
|
||||
<div className="endpoint-meta-data-label">Port</div>
|
||||
@ -177,6 +196,7 @@ function EndPointDetails({
|
||||
{!isServicesFilterApplied && (
|
||||
<DependentServices
|
||||
dependentServicesQuery={endPointDependentServicesDataQuery}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
)}
|
||||
<StatusCodeBarCharts
|
||||
@ -191,8 +211,16 @@ function EndPointDetails({
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
<StatusCodeTable endPointStatusCodeDataQuery={endPointStatusCodeDataQuery} />
|
||||
<MetricOverTimeGraph widget={rateOverTimeWidget} timeRange={timeRange} />
|
||||
<MetricOverTimeGraph widget={latencyOverTimeWidget} timeRange={timeRange} />
|
||||
<MetricOverTimeGraph
|
||||
widget={rateOverTimeWidget}
|
||||
timeRange={timeRange}
|
||||
onDragSelect={(): void => {}}
|
||||
/>
|
||||
<MetricOverTimeGraph
|
||||
widget={latencyOverTimeWidget}
|
||||
timeRange={timeRange}
|
||||
onDragSelect={(): void => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,19 +1,24 @@
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Spin, Table, Typography } from 'antd';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { Spin, Table, Tooltip, Typography } from 'antd';
|
||||
import { DEFAULT_ENTITY_VERSION, ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import {
|
||||
END_POINT_DETAILS_QUERY_KEYS_ARRAY,
|
||||
formatTopErrorsDataForTable,
|
||||
getEndPointDetailsQueryPayload,
|
||||
getTopErrorsColumnsConfig,
|
||||
getTopErrorsQueryPayload,
|
||||
TopErrorsResponseRow,
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { useMemo } from 'react';
|
||||
import { Info } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import EndPointsDropDown from './components/EndPointsDropDown';
|
||||
import ErrorState from './components/ErrorState';
|
||||
|
||||
function TopErrors({
|
||||
@ -28,9 +33,31 @@ function TopErrors({
|
||||
}): JSX.Element {
|
||||
const { startTime: minTime, endTime: maxTime } = timeRange;
|
||||
|
||||
const [endPointName, setSelectedEndPointName] = useState<string>('');
|
||||
|
||||
const queryPayloads = useMemo(
|
||||
() => getTopErrorsQueryPayload(domainName, minTime, maxTime),
|
||||
[domainName, minTime, maxTime],
|
||||
() =>
|
||||
getTopErrorsQueryPayload(domainName, minTime, maxTime, {
|
||||
items: endPointName
|
||||
? [
|
||||
{
|
||||
id: '92b8a1c1',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
op: 'AND',
|
||||
}),
|
||||
[domainName, endPointName, minTime, maxTime],
|
||||
);
|
||||
|
||||
// Since only one query here
|
||||
@ -67,6 +94,35 @@ function TopErrors({
|
||||
[topErrorsData],
|
||||
);
|
||||
|
||||
const endPointDropDownQueryPayload = useMemo(
|
||||
() => [
|
||||
getEndPointDetailsQueryPayload(domainName, minTime, maxTime, {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
})[2],
|
||||
],
|
||||
[domainName, minTime, maxTime],
|
||||
);
|
||||
|
||||
const endPointDropDownDataQueries = useQueries(
|
||||
endPointDropDownQueryPayload.map((payload) => ({
|
||||
queryKey: [
|
||||
END_POINT_DETAILS_QUERY_KEYS_ARRAY[4],
|
||||
payload,
|
||||
ENTITY_VERSION_V4,
|
||||
],
|
||||
queryFn: (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
|
||||
GetMetricQueryRange(payload, ENTITY_VERSION_V4),
|
||||
enabled: !!payload,
|
||||
staleTime: 60 * 1000, // 1 minute stale time : optimize this part
|
||||
})),
|
||||
);
|
||||
|
||||
const [endPointDropDownDataQuery] = useMemo(
|
||||
() => [endPointDropDownDataQueries[0]],
|
||||
[endPointDropDownDataQueries],
|
||||
);
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="all-endpoints-error-state-wrapper">
|
||||
@ -77,8 +133,27 @@ function TopErrors({
|
||||
|
||||
return (
|
||||
<div className="all-endpoints-container">
|
||||
<div className="top-errors-dropdown-container">
|
||||
<div className="endpoint-details-filters-container-dropdown">
|
||||
<EndPointsDropDown
|
||||
selectedEndPointName={endPointName}
|
||||
setSelectedEndPointName={setSelectedEndPointName}
|
||||
endPointDropDownDataQuery={endPointDropDownDataQuery}
|
||||
parentContainerDiv=".endpoint-details-filters-container"
|
||||
/>
|
||||
</div>
|
||||
<Tooltip title="Optionally select a specific endpoint to see status message if present">
|
||||
<Info size={16} color="white" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className="endpoints-table-container">
|
||||
<div className="endpoints-table-header">Top Errors</div>
|
||||
<div className="endpoints-table-header">
|
||||
Top Errors{' '}
|
||||
<Tooltip title="Shows top 10 errors only when status message is propagated">
|
||||
<Info size={16} color="white" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Table
|
||||
columns={topErrorsColumnsConfig}
|
||||
loading={{
|
||||
|
@ -2,6 +2,7 @@ import '../DomainDetails.styles.scss';
|
||||
|
||||
import { Table, TablePaginationConfig, Typography } from 'antd';
|
||||
import Skeleton from 'antd/lib/skeleton';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import {
|
||||
dependentServicesColumns,
|
||||
DependentServicesData,
|
||||
@ -16,10 +17,15 @@ import ErrorState from './ErrorState';
|
||||
|
||||
interface DependentServicesProps {
|
||||
dependentServicesQuery: UseQueryResult<SuccessResponse<any>, unknown>;
|
||||
timeRange: {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
};
|
||||
}
|
||||
|
||||
function DependentServices({
|
||||
dependentServicesQuery,
|
||||
timeRange,
|
||||
}: DependentServicesProps): JSX.Element {
|
||||
const {
|
||||
data,
|
||||
@ -85,6 +91,25 @@ function DependentServices({
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
onRow={(record): { onClick: () => void; className: string } => ({
|
||||
onClick: (): void => {
|
||||
const url = new URL(
|
||||
`/services/${
|
||||
record.serviceData.serviceName &&
|
||||
record.serviceData.serviceName !== '-'
|
||||
? record.serviceData.serviceName
|
||||
: ''
|
||||
}`,
|
||||
window.location.origin,
|
||||
);
|
||||
const urlQuery = new URLSearchParams();
|
||||
urlQuery.set(QueryParams.startTime, timeRange.startTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, timeRange.endTime.toString());
|
||||
url.search = urlQuery.toString();
|
||||
window.open(url.toString(), '_blank');
|
||||
},
|
||||
className: 'clickable-row',
|
||||
})}
|
||||
/>
|
||||
|
||||
{dependentServicesData.length > 5 && (
|
||||
|
@ -1,8 +1,79 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Progress, Tooltip, Typography } from 'antd';
|
||||
import { getLastUsedRelativeTime } from 'container/ApiMonitoring/utils';
|
||||
import { Progress, Skeleton, Tooltip, Typography } from 'antd';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import {
|
||||
DomainMetricsResponseRow,
|
||||
formatDomainMetricsDataForTable,
|
||||
getDomainMetricsQueryPayload,
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import ErrorState from './ErrorState';
|
||||
|
||||
function DomainMetrics({
|
||||
domainName,
|
||||
timeRange,
|
||||
}: {
|
||||
domainName: string;
|
||||
timeRange: { startTime: number; endTime: number };
|
||||
}): JSX.Element {
|
||||
const { startTime: minTime, endTime: maxTime } = timeRange;
|
||||
|
||||
const queryPayloads = useMemo(
|
||||
() => getDomainMetricsQueryPayload(domainName, minTime, maxTime),
|
||||
[domainName, minTime, maxTime],
|
||||
);
|
||||
|
||||
// Since only one query here
|
||||
const domainMetricsDataQueries = useQueries(
|
||||
queryPayloads.map((payload) => ({
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_DOMAIN_METRICS_DATA,
|
||||
payload,
|
||||
ENTITY_VERSION_V4,
|
||||
],
|
||||
queryFn: (): Promise<SuccessResponse<MetricRangePayloadProps>> =>
|
||||
GetMetricQueryRange(payload, ENTITY_VERSION_V4),
|
||||
enabled: !!payload,
|
||||
staleTime: 60 * 1000, // 1 minute stale time : optimize this part
|
||||
})),
|
||||
);
|
||||
|
||||
const domainMetricsDataQuery = domainMetricsDataQueries[0];
|
||||
// [TODO] handle the case where the data is not available
|
||||
// [TODO] Format the data properly
|
||||
const {
|
||||
data: domainMetricsData,
|
||||
isLoading,
|
||||
isRefetching,
|
||||
isError,
|
||||
refetch,
|
||||
} = domainMetricsDataQuery;
|
||||
|
||||
// [TODO] Fix type error
|
||||
const formattedDomainMetricsData = useMemo(() => {
|
||||
// Safely access the data with proper type checking
|
||||
const rowData = domainMetricsData?.payload?.data?.result[0]?.table?.rows[0];
|
||||
|
||||
// Only pass the data if it matches the expected format
|
||||
return formatDomainMetricsDataForTable(
|
||||
rowData as DomainMetricsResponseRow | undefined,
|
||||
);
|
||||
}, [domainMetricsData]);
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="all-endpoints-error-state-wrapper">
|
||||
<ErrorState refetch={refetch} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DomainMetrics({ domainData }: { domainData: any }): JSX.Element {
|
||||
return (
|
||||
<div className="domain-detail-drawer__endpoint">
|
||||
<div className="domain-details-grid">
|
||||
@ -35,41 +106,62 @@ function DomainMetrics({ domainData }: { domainData: any }): JSX.Element {
|
||||
|
||||
<div className="values-row">
|
||||
<Typography.Text className="domain-details-metadata-value">
|
||||
<Tooltip title={domainData.endpointCount}>
|
||||
<span className="round-metric-tag">{domainData.endpointCount}</span>
|
||||
</Tooltip>
|
||||
{isLoading || isRefetching ? (
|
||||
<Skeleton.Button active size="small" />
|
||||
) : (
|
||||
<Tooltip title={formattedDomainMetricsData.endpointCount}>
|
||||
<span className="round-metric-tag">
|
||||
{formattedDomainMetricsData.endpointCount}
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
{/* // update the tooltip as well */}
|
||||
<Typography.Text className="domain-details-metadata-value">
|
||||
<Tooltip title={domainData.latency}>
|
||||
<span className="round-metric-tag">
|
||||
{(domainData.latency / 1000).toFixed(3)}s
|
||||
</span>
|
||||
</Tooltip>
|
||||
{isLoading || isRefetching ? (
|
||||
<Skeleton.Button active size="small" />
|
||||
) : (
|
||||
<Tooltip title={formattedDomainMetricsData.latency}>
|
||||
<span className="round-metric-tag">
|
||||
{(Number(formattedDomainMetricsData.latency) / 1000).toFixed(3)}s
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
{/* // update the tooltip as well */}
|
||||
<Typography.Text className="domain-details-metadata-value error-rate">
|
||||
<Tooltip title={domainData.errorRate}>
|
||||
<Progress
|
||||
status="active"
|
||||
percent={Number(domainData.errorRate.toFixed(1))}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(domainData.errorRate.toFixed(1));
|
||||
if (errorRatePercent >= 90) return Color.BG_SAKURA_500;
|
||||
if (errorRatePercent >= 60) return Color.BG_AMBER_500;
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar"
|
||||
/>
|
||||
</Tooltip>
|
||||
{isLoading || isRefetching ? (
|
||||
<Skeleton.Button active size="small" />
|
||||
) : (
|
||||
<Tooltip title={formattedDomainMetricsData.errorRate}>
|
||||
<Progress
|
||||
status="active"
|
||||
percent={Number(
|
||||
Number(formattedDomainMetricsData.errorRate).toFixed(1),
|
||||
)}
|
||||
strokeLinecap="butt"
|
||||
size="small"
|
||||
strokeColor={((): string => {
|
||||
const errorRatePercent = Number(
|
||||
Number(formattedDomainMetricsData.errorRate).toFixed(1),
|
||||
);
|
||||
if (errorRatePercent >= 90) return Color.BG_SAKURA_500;
|
||||
if (errorRatePercent >= 60) return Color.BG_AMBER_500;
|
||||
return Color.BG_FOREST_500;
|
||||
})()}
|
||||
className="progress-bar"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
{/* // update the tooltip as well */}
|
||||
<Typography.Text className="domain-details-metadata-value">
|
||||
<Tooltip title={domainData.lastUsed}>
|
||||
{getLastUsedRelativeTime(domainData.lastUsed)}
|
||||
</Tooltip>
|
||||
{isLoading || isRefetching ? (
|
||||
<Skeleton.Button active size="small" />
|
||||
) : (
|
||||
<Tooltip title={formattedDomainMetricsData.lastUsed}>
|
||||
{formattedDomainMetricsData.lastUsed}
|
||||
</Tooltip>
|
||||
)}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -52,6 +52,10 @@ function EndPointsDropDown({
|
||||
: (triggerNode): HTMLElement => triggerNode.parentNode as HTMLElement
|
||||
}
|
||||
dropdownStyle={dropdownStyle}
|
||||
allowClear
|
||||
onClear={(): void => {
|
||||
setSelectedEndPointName('');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ function ExpandedRow({
|
||||
onRow={(record): { onClick: () => void; className: string } => ({
|
||||
onClick: (): void => {
|
||||
setSelectedEndPointName(record.endpointName);
|
||||
setSelectedView(VIEW_TYPES.ENDPOINT_DETAILS);
|
||||
setSelectedView(VIEW_TYPES.ENDPOINT_STATS);
|
||||
logEvent('API Monitoring: Endpoint name row clicked', {});
|
||||
},
|
||||
className: 'expanded-clickable-row',
|
||||
|
@ -5,9 +5,11 @@ import { Widgets } from 'types/api/dashboard/getAll';
|
||||
function MetricOverTimeGraph({
|
||||
widget,
|
||||
timeRange,
|
||||
onDragSelect,
|
||||
}: {
|
||||
widget: Widgets;
|
||||
timeRange: { startTime: number; endTime: number };
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
@ -16,10 +18,9 @@ function MetricOverTimeGraph({
|
||||
<GridCard
|
||||
widget={widget}
|
||||
isQueryEnabled
|
||||
onDragSelect={(): void => {}}
|
||||
onDragSelect={onDragSelect}
|
||||
customOnDragSelect={(): void => {}}
|
||||
start={timeRange.startTime}
|
||||
end={timeRange.endTime}
|
||||
customTimeRange={timeRange}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -1,11 +1,11 @@
|
||||
export enum VIEWS {
|
||||
ALL_ENDPOINTS = 'all_endpoints',
|
||||
ENDPOINT_DETAILS = 'endpoint_details',
|
||||
ENDPOINT_STATS = 'endpoint_stats',
|
||||
TOP_ERRORS = 'top_errors',
|
||||
}
|
||||
|
||||
export const VIEW_TYPES = {
|
||||
ALL_ENDPOINTS: VIEWS.ALL_ENDPOINTS,
|
||||
ENDPOINT_DETAILS: VIEWS.ENDPOINT_DETAILS,
|
||||
ENDPOINT_STATS: VIEWS.ENDPOINT_STATS,
|
||||
TOP_ERRORS: VIEWS.TOP_ERRORS,
|
||||
};
|
||||
|
@ -60,7 +60,6 @@ function DomainList({ showIP }: { showIP: boolean }): JSX.Element {
|
||||
aggregateAttribute: {
|
||||
...initialQueriesMap.traces.builder.queryData[0].aggregateAttribute,
|
||||
},
|
||||
queryName: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -333,6 +333,278 @@ export const formatDataForTable = (
|
||||
).toISOString(), // Convert from nanoseconds to milliseconds
|
||||
}));
|
||||
|
||||
export const getDomainMetricsQueryPayload = (
|
||||
domainName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
): GetQueryResultsProps[] => [
|
||||
{
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
graphType: PANEL_TYPES.TABLE,
|
||||
query: {
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'A',
|
||||
aggregateOperator: 'count',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
timeAggregation: 'rate',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '4c57937c',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'net.peer.name--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'net.peer.name',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
expression: 'A',
|
||||
disabled: false,
|
||||
stepInterval: 60,
|
||||
having: [],
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'B',
|
||||
aggregateOperator: 'p99',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id: 'duration_nano--float64----true',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
key: 'duration_nano',
|
||||
type: '',
|
||||
},
|
||||
timeAggregation: 'p99',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '2cf675cd',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'net.peer.name--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'net.peer.name',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
expression: 'B',
|
||||
disabled: false,
|
||||
stepInterval: 60,
|
||||
having: [],
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'C',
|
||||
aggregateOperator: 'count',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.String,
|
||||
id: '------false',
|
||||
isColumn: false,
|
||||
key: '',
|
||||
type: '',
|
||||
},
|
||||
timeAggregation: 'count',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '3db0f605',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'net.peer.name--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'net.peer.name',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: '6096f745',
|
||||
key: {
|
||||
dataType: DataTypes.bool,
|
||||
id: 'has_error--bool----true',
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
key: 'has_error',
|
||||
type: '',
|
||||
},
|
||||
op: '=',
|
||||
value: 'true',
|
||||
},
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
expression: 'C',
|
||||
disabled: true,
|
||||
stepInterval: 60,
|
||||
having: [],
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
{
|
||||
dataSource: DataSource.TRACES,
|
||||
queryName: 'D',
|
||||
aggregateOperator: 'max',
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'timestamp------false',
|
||||
isColumn: false,
|
||||
key: 'timestamp',
|
||||
type: '',
|
||||
},
|
||||
timeAggregation: 'max',
|
||||
spaceAggregation: 'sum',
|
||||
functions: [],
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '8ff8dea1',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'net.peer.name--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'net.peer.name',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
expression: 'D',
|
||||
disabled: false,
|
||||
stepInterval: 60,
|
||||
having: [],
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: 'avg',
|
||||
},
|
||||
],
|
||||
queryFormulas: [
|
||||
{
|
||||
queryName: 'F1',
|
||||
expression: '(C/A)*100',
|
||||
disabled: false,
|
||||
legend: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
clickhouse_sql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
id: '315b15fa-ff0c-442f-89f8-2bf4fb1af2f2',
|
||||
promql: [
|
||||
{
|
||||
disabled: false,
|
||||
legend: '',
|
||||
name: 'A',
|
||||
query: '',
|
||||
},
|
||||
],
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
},
|
||||
variables: {},
|
||||
formatForWeb: true,
|
||||
start,
|
||||
end,
|
||||
step: 60,
|
||||
},
|
||||
];
|
||||
|
||||
export interface DomainMetricsData {
|
||||
endpointCount: number | string;
|
||||
latency: number | string;
|
||||
errorRate: number | string;
|
||||
lastUsed: number | string;
|
||||
}
|
||||
|
||||
export interface DomainMetricsResponseRow {
|
||||
data: {
|
||||
A: number | string;
|
||||
B: number | string;
|
||||
D: number | string;
|
||||
F1: number | string;
|
||||
};
|
||||
}
|
||||
|
||||
export const formatDomainMetricsDataForTable = (
|
||||
row: DomainMetricsResponseRow | undefined,
|
||||
): DomainMetricsData => {
|
||||
if (!row) {
|
||||
return {
|
||||
endpointCount: '-',
|
||||
latency: '-',
|
||||
errorRate: 0,
|
||||
lastUsed: '-',
|
||||
};
|
||||
}
|
||||
return {
|
||||
endpointCount: row.data.A === 'n/a' || !row.data.A ? '-' : Number(row.data.A),
|
||||
latency:
|
||||
row.data.B === 'n/a' || row.data.B === undefined
|
||||
? '-'
|
||||
: Math.round(Number(row.data.B) / 1000000),
|
||||
errorRate: row.data.F1 === 'n/a' || !row.data.F1 ? 0 : Number(row.data.F1),
|
||||
lastUsed:
|
||||
row.data.D === 'n/a' || !row.data.D
|
||||
? '-'
|
||||
: getLastUsedRelativeTime(Math.floor(Number(row.data.D) / 1000000)),
|
||||
};
|
||||
};
|
||||
|
||||
// Rename this to a proper name
|
||||
const defaultGroupBy = [
|
||||
{
|
||||
@ -637,6 +909,7 @@ export const getTopErrorsQueryPayload = (
|
||||
domainName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
filters: IBuilderQuery['filters'],
|
||||
): GetQueryResultsProps[] => [
|
||||
{
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
@ -714,6 +987,7 @@ export const getTopErrorsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
},
|
||||
expression: 'A',
|
||||
@ -1192,7 +1466,6 @@ export const createFiltersForSelectedRowData = (
|
||||
// Sixth query payload for endpoint response status code latency bar chart
|
||||
export const getEndPointDetailsQueryPayload = (
|
||||
domainName: string,
|
||||
endPointName: string,
|
||||
start: number,
|
||||
end: number,
|
||||
filters: IBuilderQuery['filters'],
|
||||
@ -1218,19 +1491,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '92b8a1c1',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '874562e1',
|
||||
key: {
|
||||
@ -1288,19 +1548,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'B',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'c0c0f76b',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '0c5564e0',
|
||||
key: {
|
||||
@ -1358,19 +1605,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'C',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '7a3eebed',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '0d656701',
|
||||
key: {
|
||||
@ -1440,19 +1674,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'D',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'e7f12d52',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '918f5b99',
|
||||
key: {
|
||||
@ -1510,19 +1731,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'E',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '5281578a',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: 'b355d1aa',
|
||||
key: {
|
||||
@ -1634,19 +1842,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: 'e1b24204',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '212678b9',
|
||||
key: {
|
||||
@ -1713,19 +1908,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: '5dbe3518',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '212678b9',
|
||||
key: {
|
||||
@ -1909,19 +2091,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'bdac4904',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: 'b78ff216',
|
||||
key: {
|
||||
@ -1988,19 +2157,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'B',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '74f9d185',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: 'a9024472',
|
||||
key: {
|
||||
@ -2066,19 +2222,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'C',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'b7e36a72',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '1b6c062d',
|
||||
key: {
|
||||
@ -2145,19 +2288,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'D',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: 'ede7cbfe',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: 'd14792a8',
|
||||
key: {
|
||||
@ -2290,19 +2420,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: '8b1be6f0',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: '212678b9',
|
||||
key: {
|
||||
@ -2390,19 +2507,6 @@ export const getEndPointDetailsQueryPayload = (
|
||||
expression: 'A',
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
id: '52aca159',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
{
|
||||
id: 'aae93366',
|
||||
key: {
|
||||
@ -2787,7 +2891,7 @@ export const dependentServicesColumns: ColumnType<DependentServicesData>[] = [
|
||||
<div className="top-services-item">
|
||||
<div className="top-services-item-progress">
|
||||
<div className="top-services-item-key">{serviceData.serviceName}</div>
|
||||
<div className="top-services-item-count">{serviceData.count}</div>
|
||||
<div className="top-services-item-count">{serviceData.count} Calls</div>
|
||||
<div
|
||||
className="top-services-item-progress-bar"
|
||||
style={{ width: `${serviceData.percentage}%` }}
|
||||
@ -3177,10 +3281,13 @@ export const getRateOverTimeWidgetData = (
|
||||
endPointName: string,
|
||||
filters: IBuilderQuery['filters'],
|
||||
): Widgets => {
|
||||
const { endpoint, port } = extractPortAndEndpoint(endPointName);
|
||||
const legend = `${
|
||||
port !== '-' && port !== 'n/a' ? `${port}:` : ''
|
||||
}${endpoint}`;
|
||||
let legend = domainName;
|
||||
if (endPointName) {
|
||||
const { endpoint, port } = extractPortAndEndpoint(endPointName);
|
||||
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
||||
legend = `${port !== '-' && port !== 'n/a' ? `${port}:` : ''}${endpoint}`;
|
||||
}
|
||||
|
||||
return getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
title: 'Rate Over Time',
|
||||
@ -3213,34 +3320,12 @@ export const getRateOverTimeWidgetData = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: '30710f04',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend,
|
||||
limit: null,
|
||||
@ -3262,8 +3347,13 @@ export const getLatencyOverTimeWidgetData = (
|
||||
endPointName: string,
|
||||
filters: IBuilderQuery['filters'],
|
||||
): Widgets => {
|
||||
const { endpoint, port } = extractPortAndEndpoint(endPointName);
|
||||
const legend = `${port}:${endpoint}`;
|
||||
let legend = domainName;
|
||||
if (endPointName) {
|
||||
const { endpoint, port } = extractPortAndEndpoint(endPointName);
|
||||
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
||||
legend = `${port !== '-' && port !== 'n/a' ? `${port}:` : ''}${endpoint}`;
|
||||
}
|
||||
|
||||
return getWidgetQueryBuilder(
|
||||
getWidgetQuery({
|
||||
title: 'Latency Over Time',
|
||||
@ -3297,34 +3387,12 @@ export const getLatencyOverTimeWidgetData = (
|
||||
op: '=',
|
||||
value: domainName,
|
||||
},
|
||||
{
|
||||
id: '50142500',
|
||||
key: {
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
op: '=',
|
||||
value: endPointName,
|
||||
},
|
||||
...filters.items,
|
||||
],
|
||||
op: 'AND',
|
||||
},
|
||||
functions: [],
|
||||
groupBy: [
|
||||
{
|
||||
dataType: DataTypes.String,
|
||||
id: 'http.url--string--tag--false',
|
||||
isColumn: false,
|
||||
isJSON: false,
|
||||
key: 'http.url',
|
||||
type: 'tag',
|
||||
},
|
||||
],
|
||||
groupBy: [],
|
||||
having: [],
|
||||
legend,
|
||||
limit: null,
|
||||
|
@ -47,6 +47,7 @@ function GridCardGraph({
|
||||
start,
|
||||
end,
|
||||
analyticsEvent,
|
||||
customTimeRange,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
@ -130,6 +131,8 @@ function GridCardGraph({
|
||||
variables: getDashboardVariables(variables),
|
||||
fillGaps: widget.fillSpans,
|
||||
formatForWeb: widget.panelTypes === PANEL_TYPES.TABLE,
|
||||
start: customTimeRange?.startTime || start,
|
||||
end: customTimeRange?.endTime || end,
|
||||
};
|
||||
}
|
||||
updatedQuery.builder.queryData[0].pageSize = 10;
|
||||
@ -149,6 +152,8 @@ function GridCardGraph({
|
||||
initialDataSource === DataSource.TRACES && widget.selectedTracesFields,
|
||||
},
|
||||
fillGaps: widget.fillSpans,
|
||||
start: customTimeRange?.startTime || start,
|
||||
end: customTimeRange?.endTime || end,
|
||||
};
|
||||
});
|
||||
|
||||
@ -187,8 +192,8 @@ function GridCardGraph({
|
||||
variables: getDashboardVariables(variables),
|
||||
selectedTime: widget.timePreferance || 'GLOBAL_TIME',
|
||||
globalSelectedInterval,
|
||||
start,
|
||||
end,
|
||||
start: customTimeRange?.startTime || start,
|
||||
end: customTimeRange?.endTime || end,
|
||||
},
|
||||
version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
@ -202,6 +207,9 @@ function GridCardGraph({
|
||||
widget.timePreferance,
|
||||
widget.fillSpans,
|
||||
requestData,
|
||||
...(customTimeRange && customTimeRange.startTime && customTimeRange.endTime
|
||||
? [customTimeRange.startTime, customTimeRange.endTime]
|
||||
: []),
|
||||
],
|
||||
retry(failureCount, error): boolean {
|
||||
if (
|
||||
|
@ -61,6 +61,10 @@ export interface GridCardGraphProps {
|
||||
start?: number;
|
||||
end?: number;
|
||||
analyticsEvent?: string;
|
||||
customTimeRange?: {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||
|
Loading…
x
Reference in New Issue
Block a user