feat: api monitoring feedback till 28th april

This commit is contained in:
sawhil 2025-04-28 14:05:57 +05:30 committed by Sahil Khan
parent fd21a4955e
commit 6de0908a62
8 changed files with 184 additions and 40 deletions

View File

@ -1,6 +1,11 @@
import { Select } from 'antd';
import { getAllEndpointsWidgetData } from 'container/ApiMonitoring/utils';
import { initialQueriesMap } from 'constants/queryBuilder';
import {
getAllEndpointsWidgetData,
getGroupByFiltersFromGroupByValues,
} from 'container/ApiMonitoring/utils';
import GridCard from 'container/GridCardLayout/GridCard';
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
@ -15,6 +20,8 @@ function AllEndPoints({
groupBy,
setGroupBy,
timeRange,
initialFilters,
setInitialFiltersEndPointStats,
}: {
domainName: string;
setSelectedEndPointName: (name: string) => void;
@ -25,6 +32,8 @@ function AllEndPoints({
startTime: number;
endTime: number;
};
initialFilters: IBuilderQuery['filters'];
setInitialFiltersEndPointStats: (filters: IBuilderQuery['filters']) => void;
}): JSX.Element {
const {
data: groupByFiltersData,
@ -72,13 +81,81 @@ function AllEndPoints({
}
}, [groupByFiltersData]);
const currentQuery = initialQueriesMap[DataSource.TRACES];
// Local state for filters, combining endpoint filter and search filters
const [filters, setFilters] = useState<IBuilderQuery['filters']>(() => {
// Initialize filters based on the initial endPointName prop
const initialItems = [...initialFilters.items];
return { op: 'AND', items: initialItems };
});
// Handler for changes from the QueryBuilderSearchV2 component
const handleFilterChange = useCallback(
(newFilters: IBuilderQuery['filters']): void => {
// 1. Update local filters state immediately
setFilters(newFilters);
},
[], // Dependencies for the callback
);
const updatedCurrentQuery = useMemo(
() => ({
...currentQuery,
builder: {
...currentQuery.builder,
queryData: [
{
...currentQuery.builder.queryData[0],
dataSource: DataSource.TRACES,
filters, // Use the local filters state
},
],
},
}),
[filters, currentQuery],
);
const query = updatedCurrentQuery?.builder?.queryData[0] || null;
const allEndpointsWidgetData = useMemo(
() => getAllEndpointsWidgetData(groupBy, domainName),
[groupBy, domainName],
() => getAllEndpointsWidgetData(groupBy, domainName, filters),
[groupBy, domainName, filters],
);
const onRowClick = useCallback(
(props: any): void => {
setSelectedEndPointName(props[SPAN_ATTRIBUTES.URL_PATH] as string);
setSelectedView(VIEWS.ENDPOINT_STATS);
const initialItems = [
...filters.items,
...getGroupByFiltersFromGroupByValues(props, groupBy).items,
];
setInitialFiltersEndPointStats({
items: initialItems,
op: 'AND',
});
},
[
filters,
setInitialFiltersEndPointStats,
setSelectedEndPointName,
setSelectedView,
groupBy,
],
);
return (
<div className="all-endpoints-container">
<div className="all-endpoints-header">
<div className="filter-container">
<QueryBuilderSearchV2
query={query}
onChange={handleFilterChange}
placeholder="Search for filters..."
/>
</div>
</div>
<div className="group-by-container">
<div className="group-by-label"> Group by </div>
<Select
@ -100,10 +177,7 @@ function AllEndPoints({
onDragSelect={(): void => {}}
customOnDragSelect={(): void => {}}
customTimeRange={timeRange}
customOnRowClick={(props): void => {
setSelectedEndPointName(props[SPAN_ATTRIBUTES.URL_PATH] as string);
setSelectedView(VIEWS.ENDPOINT_STATS);
}}
customOnRowClick={onRowClick}
/>
</div>
</div>

View File

@ -45,6 +45,9 @@ function DomainDetails({
const [endPointsGroupBy, setEndPointsGroupBy] = useState<
IBuilderQuery['groupBy']
>([]);
const [initialFiltersEndPointStats, setInitialFiltersEndPointStats] = useState<
IBuilderQuery['filters']
>(domainListFilters);
const isDarkMode = useIsDarkMode();
const handleTabChange = (e: RadioChangeEvent): void => {
@ -160,6 +163,7 @@ function DomainDetails({
<>
<DomainMetrics
domainName={domainData.domainName}
domainListFilters={domainListFilters}
timeRange={modalTimeRange}
/>
<div className="views-tabs-container">
@ -205,6 +209,8 @@ function DomainDetails({
groupBy={endPointsGroupBy}
setGroupBy={setEndPointsGroupBy}
timeRange={modalTimeRange}
initialFilters={domainListFilters}
setInitialFiltersEndPointStats={setInitialFiltersEndPointStats}
/>
)}
@ -213,7 +219,7 @@ function DomainDetails({
domainName={domainData.domainName}
endPointName={selectedEndPointName}
setSelectedEndPointName={setSelectedEndPointName}
domainListFilters={domainListFilters}
initialFilters={initialFiltersEndPointStats}
timeRange={modalTimeRange}
handleTimeChange={handleTimeChange}
/>
@ -222,7 +228,9 @@ function DomainDetails({
{selectedView === VIEW_TYPES.TOP_ERRORS && (
<TopErrors
domainName={domainData.domainName}
initialFilters={domainListFilters}
timeRange={modalTimeRange}
handleTimeChange={handleTimeChange}
/>
)}
</>

View File

@ -41,14 +41,14 @@ function EndPointDetails({
domainName,
endPointName,
setSelectedEndPointName,
domainListFilters,
initialFilters,
timeRange,
handleTimeChange,
}: {
domainName: string;
endPointName: string;
setSelectedEndPointName: (value: string) => void;
domainListFilters: IBuilderQuery['filters'];
initialFilters: IBuilderQuery['filters'];
timeRange: {
startTime: number;
endTime: number;
@ -65,7 +65,7 @@ function EndPointDetails({
// Local state for filters, combining endpoint filter and search filters
const [filters, setFilters] = useState<IBuilderQuery['filters']>(() => {
// Initialize filters based on the initial endPointName prop
const initialItems = [];
const initialItems = [...initialFilters.items];
if (endPointName) {
initialItems.push({
id: '92b8a1c1',
@ -196,21 +196,12 @@ function EndPointDetails({
[endPointName],
);
// Combine domainListFilters (from parent) and local filters for graph/chart queries
const combinedFilters = useMemo(
() => ({
op: 'AND', // Assuming AND logic for combining filter sets
items: [...domainListFilters.items, ...filters.items],
}),
[domainListFilters, filters],
);
const [rateOverTimeWidget, latencyOverTimeWidget] = useMemo(
() => [
getRateOverTimeWidgetData(domainName, endPointName, combinedFilters),
getLatencyOverTimeWidgetData(domainName, endPointName, combinedFilters),
getRateOverTimeWidgetData(domainName, endPointName, filters),
getLatencyOverTimeWidgetData(domainName, endPointName, filters),
],
[domainName, endPointName, combinedFilters], // Use combinedFilters
[domainName, endPointName, filters], // Use combinedFilters
);
// // [TODO] Fix this later
@ -273,7 +264,6 @@ function EndPointDetails({
}
domainName={domainName}
endPointName={endPointName}
domainListFilters={domainListFilters}
filters={filters}
timeRange={timeRange}
onDragSelect={onDragSelect}

View File

@ -10,13 +10,18 @@ import {
getTopErrorsQueryPayload,
TopErrorsResponseRow,
} from 'container/ApiMonitoring/utils';
import {
CustomTimeType,
Time,
} from 'container/TopNav/DateTimeSelectionV2/config';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { Info } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useEffect, 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 { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import EndPointsDropDown from './components/EndPointsDropDown';
import ErrorState from './components/ErrorState';
@ -25,12 +30,19 @@ import { SPAN_ATTRIBUTES } from './constants';
function TopErrors({
domainName,
timeRange,
initialFilters,
handleTimeChange,
}: {
domainName: string;
timeRange: {
startTime: number;
endTime: number;
};
initialFilters: IBuilderQuery['filters'];
handleTimeChange: (
interval: Time | CustomTimeType,
dateTimeRange?: [number, number],
) => void;
}): JSX.Element {
const { startTime: minTime, endTime: maxTime } = timeRange;
@ -53,11 +65,12 @@ function TopErrors({
op: '=',
value: endPointName,
},
...initialFilters.items,
]
: [],
op: 'AND',
}),
[domainName, endPointName, minTime, maxTime],
[domainName, endPointName, minTime, maxTime, initialFilters],
);
// Since only one query here
@ -123,6 +136,10 @@ function TopErrors({
[endPointDropDownDataQueries],
);
useEffect(() => {
handleTimeChange('6h');
}, [handleTimeChange]);
if (isError) {
return (
<div className="all-endpoints-error-state-wrapper">

View File

@ -12,21 +12,30 @@ import { useMemo } from 'react';
import { useQueries } from 'react-query';
import { SuccessResponse } from 'types/api';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import ErrorState from './ErrorState';
function DomainMetrics({
domainName,
timeRange,
domainListFilters,
}: {
domainName: string;
timeRange: { startTime: number; endTime: number };
domainListFilters: IBuilderQuery['filters'];
}): JSX.Element {
const { startTime: minTime, endTime: maxTime } = timeRange;
const queryPayloads = useMemo(
() => getDomainMetricsQueryPayload(domainName, minTime, maxTime),
[domainName, minTime, maxTime],
() =>
getDomainMetricsQueryPayload(
domainName,
minTime,
maxTime,
domainListFilters,
),
[domainName, minTime, maxTime, domainListFilters],
);
// Since only one query here

View File

@ -33,7 +33,6 @@ function StatusCodeBarCharts({
endPointStatusCodeLatencyBarChartsDataQuery,
domainName,
endPointName,
domainListFilters,
filters,
timeRange,
onDragSelect,
@ -48,7 +47,6 @@ function StatusCodeBarCharts({
>;
domainName: string;
endPointName: string;
domainListFilters: IBuilderQuery['filters'];
filters: IBuilderQuery['filters'];
timeRange: {
startTime: number;
@ -132,10 +130,10 @@ function StatusCodeBarCharts({
const widget = useMemo<Widgets>(
() =>
getStatusCodeBarChartWidgetData(domainName, endPointName, {
items: [...domainListFilters.items, ...filters.items],
items: [...filters.items],
op: filters.op,
}),
[domainName, endPointName, domainListFilters, filters],
[domainName, endPointName, filters],
);
const graphClickHandler = useCallback(

View File

@ -39,7 +39,9 @@ function DomainList({ showIP }: { showIP: boolean }): JSX.Element {
);
const { currentQuery, handleRunQuery } = useQueryBuilder();
const query = currentQuery?.builder?.queryData[0] || null;
const query = useMemo(() => currentQuery?.builder?.queryData[0] || null, [
currentQuery,
]);
const { handleChangeQueryData } = useQueryOperations({
index: 0,
@ -96,7 +98,7 @@ function DomainList({ showIP }: { showIP: boolean }): JSX.Element {
op: '=',
value: 'Client',
},
...(query?.filters.items || []),
...(compositeData?.builder?.queryData[0]?.filters.items || []),
],
},
};

View File

@ -339,6 +339,7 @@ export const getDomainMetricsQueryPayload = (
domainName: string,
start: number,
end: number,
filters: IBuilderQuery['filters'],
): GetQueryResultsProps[] => [
{
selectedTime: 'GLOBAL_TIME',
@ -374,6 +375,7 @@ export const getDomainMetricsQueryPayload = (
op: '=',
value: domainName,
},
...filters.items,
],
op: 'AND',
},
@ -415,6 +417,7 @@ export const getDomainMetricsQueryPayload = (
op: '=',
value: domainName,
},
...filters.items,
],
op: 'AND',
},
@ -468,6 +471,7 @@ export const getDomainMetricsQueryPayload = (
op: '=',
value: 'true',
},
...filters.items,
],
op: 'AND',
},
@ -509,6 +513,7 @@ export const getDomainMetricsQueryPayload = (
op: '=',
value: domainName,
},
...filters.items,
],
op: 'AND',
},
@ -1379,9 +1384,10 @@ export const getTopErrorsColumnsConfig = (): ColumnType<TopErrorsTableRowData>[]
ellipsis: true,
sorter: false,
className: 'column',
render: (text: string, record: TopErrorsTableRowData): React.ReactNode => (
<div className="endpoint-name-value">{record.endpointName}</div>
),
render: (text: string, record: TopErrorsTableRowData): React.ReactNode => {
const { endpoint } = extractPortAndEndpoint(record.endpointName);
return <div className="endpoint-name-value">{endpoint}</div>;
},
},
{
title: <div className="column-header">Status code</div>,
@ -2730,7 +2736,7 @@ export const endPointStatusCodeColumns: ColumnType<EndPointStatusCodeData>[] = [
},
},
{
title: 'P99',
title: 'P99 Latency',
dataIndex: 'p99Latency',
key: 'p99Latency',
align: 'right',
@ -2741,6 +2747,7 @@ export const endPointStatusCodeColumns: ColumnType<EndPointStatusCodeData>[] = [
b.p99Latency === '-' || b.p99Latency === 'n/a' ? 0 : Number(b.p99Latency);
return p99LatencyA - p99LatencyB;
},
render: (latency: number): ReactNode => <span>{latency || '-'}ms</span>,
},
];
@ -3222,6 +3229,7 @@ export const END_POINT_DETAILS_QUERY_KEYS_ARRAY = [
export const getAllEndpointsWidgetData = (
groupBy: BaseAutocompleteData[],
domainName: string,
filters: IBuilderQuery['filters'],
// eslint-disable-next-line sonarjs/cognitive-complexity
): Widgets => {
const isGroupedByAttribute = groupBy.length > 0;
@ -3270,6 +3278,7 @@ export const getAllEndpointsWidgetData = (
op: '=',
value: 'Client',
},
...filters.items,
],
op: 'AND',
},
@ -3325,6 +3334,7 @@ export const getAllEndpointsWidgetData = (
op: '=',
value: 'Client',
},
...filters.items,
],
op: 'AND',
},
@ -3380,6 +3390,7 @@ export const getAllEndpointsWidgetData = (
op: '=',
value: 'Client',
},
...filters.items,
],
op: 'AND',
},
@ -3447,6 +3458,7 @@ export const getAllEndpointsWidgetData = (
op: '=',
value: 'Client',
},
...filters.items,
],
op: 'AND',
},
@ -3479,9 +3491,12 @@ export const getAllEndpointsWidgetData = (
widget.renderColumnCell = {
[SPAN_ATTRIBUTES.URL_PATH]: (url: any): ReactNode => {
const { endpoint } = extractPortAndEndpoint(url);
const { endpoint, port } = extractPortAndEndpoint(url);
return (
<span>{endpoint === 'n/a' || url === undefined ? '-' : endpoint}</span>
<span>
{port !== '-' && port !== 'n/a' ? `:${port}` : ''}
{endpoint === 'n/a' || url === undefined ? '-' : endpoint}
</span>
);
},
A: (numOfCalls: any): ReactNode => (
@ -3563,6 +3578,37 @@ export const getAllEndpointsWidgetData = (
return widget;
};
const keysToRemove = ['http.url', 'A', 'B', 'C', 'F1'];
export const getGroupByFiltersFromGroupByValues = (
rowData: any,
groupBy: BaseAutocompleteData[],
): IBuilderQuery['filters'] => {
const items = Object.keys(rowData)
.filter((key) => !keysToRemove.includes(key))
.map((key) => {
const groupByAttribute = groupBy.find((gb) => gb.key === key);
return {
id: groupByAttribute?.id || v4(),
key: {
dataType: groupByAttribute?.dataType || DataTypes.String,
isColumn: groupByAttribute?.isColumn || true,
isJSON: groupByAttribute?.isJSON || false,
key: groupByAttribute?.key || key,
type: groupByAttribute?.type || '',
},
op: '=', // operator for every attribute -> discuss
value: rowData[key],
};
});
console.log('uncaught items', items);
return {
items,
op: 'AND',
};
};
export const getRateOverTimeWidgetData = (
domainName: string,
endPointName: string,