Compare commits

...

9 Commits

12 changed files with 351 additions and 470 deletions

View File

@@ -8,19 +8,13 @@ import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import LogsError from 'container/LogsError/LogsError';
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useHandleLogsPagination } from 'hooks/infraMonitoring/useHandleLogsPagination';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { isEqual } from 'lodash-es';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useQuery } from 'react-query';
import { Virtuoso } from 'react-virtuoso';
import { ILog } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import { v4 } from 'uuid';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { getHostLogsQueryPayload } from './constants';
import NoLogsContainer from './NoLogsContainer';
@@ -30,51 +24,30 @@ interface Props {
startTime: number;
endTime: number;
};
handleChangeLogFilters: (filters: IBuilderQuery['filters']) => void;
filters: IBuilderQuery['filters'];
}
function HostMetricsLogs({
timeRange,
handleChangeLogFilters,
filters,
}: Props): JSX.Element {
const [logs, setLogs] = useState<ILog[]>([]);
const [hasReachedEndOfLogs, setHasReachedEndOfLogs] = useState(false);
const [restFilters, setRestFilters] = useState<TagFilterItem[]>([]);
const [resetLogsList, setResetLogsList] = useState<boolean>(false);
useEffect(() => {
const newRestFilters = filters.items.filter(
(item) => item.key?.key !== 'id' && item.key?.key !== 'host.name',
);
const areFiltersSame = isEqual(restFilters, newRestFilters);
if (!areFiltersSame) {
setResetLogsList(true);
}
setRestFilters(newRestFilters);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters]);
const queryPayload = useMemo(() => {
const basePayload = getHostLogsQueryPayload(
timeRange.startTime,
timeRange.endTime,
filters,
);
basePayload.query.builder.queryData[0].pageSize = 100;
basePayload.query.builder.queryData[0].orderBy = [
{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC },
];
return basePayload;
}, [timeRange.startTime, timeRange.endTime, filters]);
const [isPaginating, setIsPaginating] = useState(false);
function HostMetricsLogs({ timeRange, filters }: Props): JSX.Element {
const basePayload = getHostLogsQueryPayload(
timeRange.startTime,
timeRange.endTime,
filters,
);
const {
logs,
hasReachedEndOfLogs,
isPaginating,
currentPage,
setIsPaginating,
handleNewData,
loadMoreLogs,
queryPayload,
} = useHandleLogsPagination({
timeRange,
filters,
excludeFilterKeys: ['host.name'],
basePayload,
});
const { data, isLoading, isFetching, isError } = useQuery({
queryKey: [
@@ -82,6 +55,7 @@ function HostMetricsLogs({
timeRange.startTime,
timeRange.endTime,
filters,
currentPage,
],
queryFn: () => GetMetricQueryRange(queryPayload, DEFAULT_ENTITY_VERSION),
enabled: !!queryPayload,
@@ -90,33 +64,13 @@ function HostMetricsLogs({
useEffect(() => {
if (data?.payload?.data?.newResult?.data?.result) {
const currentData = data.payload.data.newResult.data.result;
if (resetLogsList) {
const currentLogs: ILog[] =
currentData[0].list?.map((item) => ({
...item.data,
timestamp: item.timestamp,
})) || [];
setLogs(currentLogs);
setResetLogsList(false);
}
if (currentData.length > 0 && currentData[0].list) {
const currentLogs: ILog[] =
currentData[0].list.map((item) => ({
...item.data,
timestamp: item.timestamp,
})) || [];
setLogs((prev) => [...prev, ...currentLogs]);
} else {
setHasReachedEndOfLogs(true);
}
handleNewData(data.payload.data.newResult.data.result);
}
}, [data, restFilters, isPaginating, resetLogsList]);
}, [data, handleNewData]);
useEffect(() => {
setIsPaginating(false);
}, [data, setIsPaginating]);
const getItemContent = useCallback(
(_: number, logToRender: ILog): JSX.Element => (
@@ -144,39 +98,6 @@ function HostMetricsLogs({
[],
);
const loadMoreLogs = useCallback(() => {
if (!logs.length) return;
setIsPaginating(true);
const lastLog = logs[logs.length - 1];
const newItems = [
...filters.items.filter((item) => item.key?.key !== 'id'),
{
id: v4(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: '<',
value: lastLog.id,
},
];
const newFilters = {
op: 'AND',
items: newItems,
} as IBuilderQuery['filters'];
handleChangeLogFilters(newFilters);
}, [logs, filters, handleChangeLogFilters]);
useEffect(() => {
setIsPaginating(false);
}, [data]);
const renderFooter = useCallback(
(): JSX.Element | null => (
// eslint-disable-next-line react/jsx-no-useless-fragment

View File

@@ -7,6 +7,7 @@ import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { EventContents } from 'container/InfraMonitoringK8s/commonUtils';
import { K8sCategory } from 'container/InfraMonitoringK8s/constants';
import LoadingContainer from 'container/InfraMonitoringK8s/LoadingContainer';
import { INITIAL_PAGE_SIZE } from 'container/LogsContextList/configs';
import LogsError from 'container/LogsError/LogsError';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
@@ -21,10 +22,8 @@ import { isArray } from 'lodash-es';
import { ChevronDown, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { v4 } from 'uuid';
import {
EntityDetailsEmptyContainer,
@@ -123,16 +122,19 @@ export default function Events({
filters,
);
basePayload.query.builder.queryData[0].pageSize = 10;
basePayload.query.builder.queryData[0].pageSize = INITIAL_PAGE_SIZE;
basePayload.query.builder.queryData[0].offset =
(page - 1) * INITIAL_PAGE_SIZE;
basePayload.query.builder.queryData[0].orderBy = [
{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC },
{ columnName: 'id', order: ORDERBY_FILTERS.DESC },
];
return basePayload;
}, [timeRange.startTime, timeRange.endTime, filters]);
}, [timeRange.startTime, timeRange.endTime, filters, page]);
const { data: eventsData, isLoading, isFetching, isError } = useQuery({
queryKey: [queryKey, timeRange.startTime, timeRange.endTime, filters],
queryKey: [queryKey, timeRange.startTime, timeRange.endTime, filters, page],
queryFn: () => GetMetricQueryRange(queryPayload, DEFAULT_ENTITY_VERSION),
enabled: !!queryPayload,
});
@@ -189,61 +191,12 @@ export default function Events({
const handlePrev = (): void => {
if (!formattedEntityEvents.length) return;
setPage(page - 1);
const firstEvent = formattedEntityEvents[0];
const newItems = [
...filters.items.filter((item) => item.key?.key !== 'id'),
{
id: v4(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: '>',
value: firstEvent.id,
},
];
const newFilters = {
op: 'AND',
items: newItems,
} as IBuilderQuery['filters'];
handleChangeEventFilters(newFilters);
};
const handleNext = (): void => {
if (!formattedEntityEvents.length) return;
setPage(page + 1);
const lastEvent = formattedEntityEvents[formattedEntityEvents.length - 1];
const newItems = [
...filters.items.filter((item) => item.key?.key !== 'id'),
{
id: v4(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: '<',
value: lastEvent.id,
},
];
const newFilters = {
op: 'AND',
items: newItems,
} as IBuilderQuery['filters'];
handleChangeEventFilters(newFilters);
};
const handleExpandRowIcon = ({

View File

@@ -9,19 +9,13 @@ import { K8sCategory } from 'container/InfraMonitoringK8s/constants';
import LogsError from 'container/LogsError/LogsError';
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useHandleLogsPagination } from 'hooks/infraMonitoring/useHandleLogsPagination';
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
import { isEqual } from 'lodash-es';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import { useQuery } from 'react-query';
import { Virtuoso } from 'react-virtuoso';
import { ILog } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import { v4 } from 'uuid';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import {
EntityDetailsEmptyContainer,
@@ -33,7 +27,6 @@ interface Props {
startTime: number;
endTime: number;
};
handleChangeLogFilters: (filters: IBuilderQuery['filters']) => void;
filters: IBuilderQuery['filters'];
queryKey: string;
category: K8sCategory;
@@ -42,87 +35,33 @@ interface Props {
function EntityLogs({
timeRange,
handleChangeLogFilters,
filters,
queryKey,
category,
queryKeyFilters,
}: Props): JSX.Element {
const [logs, setLogs] = useState<ILog[]>([]);
const [hasReachedEndOfLogs, setHasReachedEndOfLogs] = useState(false);
const [restFilters, setRestFilters] = useState<TagFilterItem[]>([]);
const [resetLogsList, setResetLogsList] = useState<boolean>(false);
const basePayload = getEntityEventsOrLogsQueryPayload(
timeRange.startTime,
timeRange.endTime,
filters,
);
useEffect(() => {
const newRestFilters = filters.items.filter(
(item) =>
item.key?.key !== 'id' && !queryKeyFilters.includes(item.key?.key ?? ''),
);
const areFiltersSame = isEqual(restFilters, newRestFilters);
if (!areFiltersSame) {
setResetLogsList(true);
}
setRestFilters(newRestFilters);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters]);
const queryPayload = useMemo(() => {
const basePayload = getEntityEventsOrLogsQueryPayload(
timeRange.startTime,
timeRange.endTime,
filters,
);
basePayload.query.builder.queryData[0].pageSize = 100;
basePayload.query.builder.queryData[0].orderBy = [
{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC },
];
return basePayload;
}, [timeRange.startTime, timeRange.endTime, filters]);
const [isPaginating, setIsPaginating] = useState(false);
const { data, isLoading, isFetching, isError } = useQuery({
queryKey: [queryKey, timeRange.startTime, timeRange.endTime, filters],
queryFn: () => GetMetricQueryRange(queryPayload, DEFAULT_ENTITY_VERSION),
enabled: !!queryPayload,
keepPreviousData: isPaginating,
const {
logs,
hasReachedEndOfLogs,
isPaginating,
currentPage,
setIsPaginating,
handleNewData,
loadMoreLogs,
queryPayload,
} = useHandleLogsPagination({
timeRange,
filters,
queryKeyFilters,
basePayload,
});
useEffect(() => {
if (data?.payload?.data?.newResult?.data?.result) {
const currentData = data.payload.data.newResult.data.result;
if (resetLogsList) {
const currentLogs: ILog[] =
currentData[0].list?.map((item) => ({
...item.data,
timestamp: item.timestamp,
})) || [];
setLogs(currentLogs);
setResetLogsList(false);
}
if (currentData.length > 0 && currentData[0].list) {
const currentLogs: ILog[] =
currentData[0].list.map((item) => ({
...item.data,
timestamp: item.timestamp,
})) || [];
setLogs((prev) => [...prev, ...currentLogs]);
} else {
setHasReachedEndOfLogs(true);
}
}
}, [data, restFilters, isPaginating, resetLogsList]);
const getItemContent = useCallback(
(_: number, logToRender: ILog): JSX.Element => (
<RawLogView
@@ -149,38 +88,28 @@ function EntityLogs({
[],
);
const loadMoreLogs = useCallback(() => {
if (!logs.length) return;
const { data, isLoading, isFetching, isError } = useQuery({
queryKey: [
queryKey,
timeRange.startTime,
timeRange.endTime,
filters,
currentPage,
],
queryFn: () => GetMetricQueryRange(queryPayload, DEFAULT_ENTITY_VERSION),
enabled: !!queryPayload,
keepPreviousData: isPaginating,
});
setIsPaginating(true);
const lastLog = logs[logs.length - 1];
const newItems = [
...filters.items.filter((item) => item.key?.key !== 'id'),
{
id: v4(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: '<',
value: lastLog.id,
},
];
const newFilters = {
op: 'AND',
items: newItems,
} as IBuilderQuery['filters'];
handleChangeLogFilters(newFilters);
}, [logs, filters, handleChangeLogFilters]);
useEffect(() => {
if (data?.payload?.data?.newResult?.data?.result) {
handleNewData(data.payload.data.newResult.data.result);
}
}, [data, handleNewData]);
useEffect(() => {
setIsPaginating(false);
}, [data]);
}, [data, setIsPaginating]);
const renderFooter = useCallback(
(): JSX.Element | null => (

View File

@@ -96,7 +96,6 @@ function EntityLogsDetailedView({
</div>
<EntityLogs
timeRange={timeRange}
handleChangeLogFilters={handleChangeLogFilters}
filters={logFilters}
queryKey={queryKey}
category={category}

View File

@@ -11,7 +11,8 @@ import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { uniqBy } from 'lodash-es';
import { useCallback, useMemo, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { ILog } from 'types/api/logs/log';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
@@ -27,7 +28,6 @@ function ContextLogRenderer({
}: ContextLogRendererProps): JSX.Element {
const [prevLogPage, setPrevLogPage] = useState<number>(1);
const [afterLogPage, setAfterLogPage] = useState<number>(1);
const [logs, setLogs] = useState<ILog[]>([log]);
const { initialDataSource, stagedQuery } = useQueryBuilder();
@@ -73,20 +73,10 @@ function ContextLogRenderer({
fontSize: options.fontSize,
});
useEffect(() => {
setLogs((prev) => [...previousLogs, ...prev]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [previousLogs]);
useEffect(() => {
setLogs((prev) => [...prev, ...afterLogs]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [afterLogs]);
useEffect(() => {
setLogs([log]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters]);
const logsToRender = useMemo(
() => uniqBy([...previousLogs, log, ...afterLogs], 'id'),
[previousLogs, log, afterLogs],
);
const lengthMultipier = useMemo(() => {
switch (options.fontSize) {
@@ -137,9 +127,9 @@ function ContextLogRenderer({
<Virtuoso
className="virtuoso-list"
initialTopMostItemIndex={0}
data={logs}
data={logsToRender}
itemContent={getItemContent}
style={{ height: `calc(${logs.length} * ${lengthMultipier}px)` }}
style={{ height: `calc(${logsToRender.length} * ${lengthMultipier}px)` }}
/>
</OverlayScrollbar>
{isAfterLogsFetching && (

View File

@@ -40,7 +40,7 @@ import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { FlatLogData } from 'lib/logs/flatLogData';
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
import { getPaginationQueryDataV2 } from 'lib/newQueryBuilder/getPaginationQueryData';
import {
cloneDeep,
defaultTo,
@@ -94,7 +94,9 @@ function LogsExplorerViews({
selectedView: SELECTED_VIEWS;
showFrequencyChart: boolean;
setIsLoadingQueries: React.Dispatch<React.SetStateAction<boolean>>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
listQueryKeyRef: MutableRefObject<any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
chartQueryKeyRef: MutableRefObject<any>;
}): JSX.Element {
const { notifications } = useNotifications();
@@ -305,10 +307,7 @@ function LogsExplorerViews({
): Query | null => {
if (!query) return null;
const paginateData = getPaginationQueryData({
filters: params.filters,
listItemId: params.log ? params.log.id : null,
orderByTimestamp,
const paginateData = getPaginationQueryDataV2({
page: params.page,
pageSize: params.pageSize,
});
@@ -333,7 +332,7 @@ function LogsExplorerViews({
return data;
},
[orderByTimestamp, listQuery],
[listQuery],
);
const handleEndReached = useCallback(

View File

@@ -10,7 +10,6 @@ import Controls from 'container/Controls';
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
import { tableStyles } from 'container/TracesExplorer/ListView/styles';
import { useActiveLog } from 'hooks/logs/useActiveLog';
import { Pagination } from 'hooks/queryPagination';
import { useLogsData } from 'hooks/useLogsData';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { FlatLogData } from 'lib/logs/flatLogData';
@@ -21,46 +20,27 @@ import {
HTMLAttributes,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { ILog } from 'types/api/logs/log';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { getLogPanelColumnsList, getNextOrPreviousItems } from './utils';
import { getLogPanelColumnsList } from './utils';
function LogsPanelComponent({
widget,
setRequestData,
queryResponse,
}: LogsPanelComponentProps): JSX.Element {
const [pagination, setPagination] = useState<Pagination>({
offset: 0,
limit: widget.query.builder.queryData[0].limit || 0,
});
useEffect(() => {
setRequestData((prev) => ({
...prev,
tableParams: {
pagination,
},
}));
}, [pagination, setRequestData]);
const [pageSize, setPageSize] = useState<number>(10);
const [offset, setOffset] = useState<number>(0);
const handleChangePageSize = (value: number): void => {
setPagination({
...pagination,
limit: 0,
offset: value,
});
setPageSize(value);
setOffset(0);
setRequestData((prev) => {
const newQueryData = { ...prev.query };
newQueryData.builder.queryData[0].pageSize = value;
@@ -70,7 +50,7 @@ function LogsPanelComponent({
tableParams: {
pagination: {
limit: 0,
offset: value,
offset: 0,
},
},
};
@@ -88,22 +68,12 @@ function LogsPanelComponent({
queryResponse.data?.payload?.data?.newResult?.data?.result[0]?.list?.length;
const totalCount = useMemo(() => dataLength || 0, [dataLength]);
const [firstLog, setFirstLog] = useState<ILog>();
const [lastLog, setLastLog] = useState<ILog>();
const { logs } = useLogsData({
result: queryResponse.data?.payload?.data?.newResult?.data?.result,
panelType: PANEL_TYPES.LIST,
stagedQuery: widget.query,
});
useEffect(() => {
if (logs.length) {
setFirstLog(logs[0]);
setLastLog(logs[logs.length - 1]);
}
}, [logs]);
const flattenLogData = useMemo(
() => logs.map((log) => FlatLogData(log) as RowData),
[logs],
@@ -127,84 +97,27 @@ function LogsPanelComponent({
[logs, onSetActiveLog],
);
const isOrderByTimeStamp =
widget.query.builder.queryData[0].orderBy.length > 0 &&
widget.query.builder.queryData[0].orderBy[0].columnName === 'timestamp';
const handleRequestData = (newOffset: number): void => {
setOffset(newOffset);
setRequestData((prev) => ({
...prev,
tableParams: {
pagination: {
limit: widget.query.builder.queryData[0].limit || 0,
offset: newOffset < 0 ? 0 : newOffset,
},
},
}));
};
const handlePreviousPagination = (): void => {
if (isOrderByTimeStamp) {
setRequestData((prev) => ({
...prev,
query: {
...prev.query,
builder: {
...prev.query.builder,
queryData: [
{
...prev.query.builder.queryData[0],
filters: {
...prev.query.builder.queryData[0].filters,
items: [
...getNextOrPreviousItems(
prev.query.builder.queryData[0].filters.items,
'PREV',
firstLog,
),
],
},
limit: 0,
offset: 0,
},
],
},
},
}));
}
if (!isOrderByTimeStamp) {
setPagination({
...pagination,
limit: 0,
offset: pagination.offset - pageSize,
});
}
const newOffset = offset - pageSize;
handleRequestData(newOffset);
};
const handleNextPagination = (): void => {
if (isOrderByTimeStamp) {
setRequestData((prev) => ({
...prev,
query: {
...prev.query,
builder: {
...prev.query.builder,
queryData: [
{
...prev.query.builder.queryData[0],
filters: {
...prev.query.builder.queryData[0].filters,
items: [
...getNextOrPreviousItems(
prev.query.builder.queryData[0].filters.items,
'NEXT',
lastLog,
),
],
},
limit: 0,
offset: 0,
},
],
},
},
}));
}
if (!isOrderByTimeStamp) {
setPagination({
...pagination,
limit: 0,
offset: pagination.offset + pageSize,
});
}
const newOffset = offset + pageSize;
handleRequestData(newOffset);
};
if (queryResponse.isError) {
@@ -235,12 +148,11 @@ function LogsPanelComponent({
totalCount={totalCount}
perPageOptions={PER_PAGE_OPTIONS}
isLoading={queryResponse.isFetching}
offset={pagination.offset}
offset={offset}
countPerPage={pageSize}
handleNavigatePrevious={handlePreviousPagination}
handleNavigateNext={handleNextPagination}
handleCountItemsPerPageChange={handleChangePageSize}
isLogPanel={isOrderByTimeStamp}
/>
</div>
)}

View File

@@ -1,16 +1,11 @@
import { ColumnsType } from 'antd/es/table';
import { Typography } from 'antd/lib';
import { OPERATORS } from 'constants/queryBuilder';
import { TimestampInput } from 'hooks/useTimezoneFormatter/useTimezoneFormatter';
// import Typography from 'antd/es/typography/Typography';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { ReactNode } from 'react';
import { Widgets } from 'types/api/dashboard/getAll';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { v4 as uuid } from 'uuid';
export const getLogPanelColumnsList = (
selectedLogFields: Widgets['selectedLogFields'],
@@ -55,49 +50,3 @@ export const getLogPanelColumnsList = (
return [...initialColumns, ...columns];
};
export const getNextOrPreviousItems = (
items: TagFilterItem[],
direction: 'NEXT' | 'PREV',
log?: ILog,
): TagFilterItem[] => {
const nextItem = {
id: uuid(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: OPERATORS['<'],
value: log?.id || '',
};
const prevItem = {
id: uuid(),
key: {
key: 'id',
type: '',
dataType: DataTypes.String,
isColumn: true,
},
op: OPERATORS['>'],
value: log?.id || '',
};
let index = items.findIndex((item) => item.op === OPERATORS['<']);
if (index === -1) {
index = items.findIndex((item) => item.op === OPERATORS['>']);
}
if (index === -1) {
if (direction === 'NEXT') {
return [...items, nextItem];
}
return [...items, prevItem];
}
const newItems = [...items];
if (direction === 'NEXT') {
newItems[index] = nextItem;
} else {
newItems[index] = prevItem;
}
return newItems;
};

View File

@@ -0,0 +1,181 @@
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { isEqual } from 'lodash-es';
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { ILog } from 'types/api/logs/log';
import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
interface TimeRange {
startTime: number;
endTime: number;
}
interface UsePaginatedLogsProps {
timeRange: TimeRange;
filters: IBuilderQuery['filters'];
queryKeyFilters?: string[];
excludeFilterKeys?: string[];
basePayload: GetQueryResultsProps;
}
interface UseHandleLogsPagination {
logs: ILog[];
hasReachedEndOfLogs: boolean;
isPaginating: boolean;
currentPage: number;
resetLogsList: boolean;
setIsPaginating: Dispatch<SetStateAction<boolean>>;
handleNewData: (currentData: any) => void;
loadMoreLogs: () => void;
shouldResetPage: boolean;
queryPayload: GetQueryResultsProps;
}
export const useHandleLogsPagination = ({
timeRange,
filters,
queryKeyFilters = [],
excludeFilterKeys = [],
basePayload,
}: UsePaginatedLogsProps): UseHandleLogsPagination => {
const [logs, setLogs] = useState<ILog[]>([]);
const [hasReachedEndOfLogs, setHasReachedEndOfLogs] = useState(false);
const [restFilters, setRestFilters] = useState<TagFilterItem[]>([]);
const [resetLogsList, setResetLogsList] = useState<boolean>(false);
const [page, setPage] = useState(1);
const [prevTimeRange, setPrevTimeRange] = useState<TimeRange | null>(
timeRange,
);
const [isPaginating, setIsPaginating] = useState(false);
const { shouldResetPage, newRestFilters } = useMemo(() => {
const newRestFilters = filters.items.filter((item) => {
const keyToCheck = item.key?.key ?? '';
return (
!queryKeyFilters.includes(keyToCheck) &&
!excludeFilterKeys.includes(keyToCheck)
);
});
const areFiltersSame = isEqual(restFilters, newRestFilters);
const shouldResetPage =
!areFiltersSame ||
timeRange.startTime !== prevTimeRange?.startTime ||
timeRange.endTime !== prevTimeRange?.endTime;
return { shouldResetPage, newRestFilters };
}, [
filters,
timeRange,
prevTimeRange,
queryKeyFilters,
excludeFilterKeys,
restFilters,
]);
const currentPage = useMemo(() => {
if (shouldResetPage) {
return 1;
}
return page;
}, [shouldResetPage, page]);
// Handle data updates
const handleNewData = useCallback(
(currentData: any) => {
if (!currentData[0].list) {
setHasReachedEndOfLogs(true);
return;
}
const currentLogs: ILog[] =
currentData[0].list?.map((item: any) => ({
...item.data,
timestamp: item.timestamp,
})) || [];
if (resetLogsList) {
setLogs(currentLogs);
setResetLogsList(false);
return;
}
const newLogs = currentLogs.filter(
(newLog) => !logs.some((existingLog) => isEqual(existingLog, newLog)),
);
if (newLogs.length > 0) {
setLogs((prev) => [...prev, ...newLogs]);
}
},
[logs, resetLogsList],
);
// Reset logic
useEffect(() => {
if (shouldResetPage) {
setPage(1);
setLogs([]);
setResetLogsList(true);
}
setPrevTimeRange(timeRange);
setRestFilters(newRestFilters);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [shouldResetPage, timeRange]);
const loadMoreLogs = useCallback(() => {
if (!logs.length) return;
setPage((prev) => prev + 1);
setIsPaginating(true);
}, [logs]);
const queryPayload = useMemo(
() => ({
...basePayload,
query: {
...basePayload.query,
builder: {
...basePayload.query.builder,
queryData: [
{
...basePayload.query.builder.queryData[0],
pageSize: DEFAULT_PER_PAGE_VALUE,
offset: (currentPage - 1) * DEFAULT_PER_PAGE_VALUE,
orderBy: [
{ columnName: 'timestamp', order: ORDERBY_FILTERS.DESC },
{ columnName: 'id', order: ORDERBY_FILTERS.DESC },
],
},
],
},
},
}),
[basePayload, currentPage],
);
return {
logs,
hasReachedEndOfLogs,
isPaginating,
currentPage,
resetLogsList,
queryPayload,
setIsPaginating,
handleNewData,
loadMoreLogs,
shouldResetPage,
};
};

View File

@@ -22,16 +22,46 @@ export const useGetQueryRange: UseGetQueryRange = (
options,
headers,
) => {
const newRequestData: GetQueryResultsProps = useMemo(
() => ({
const newRequestData: GetQueryResultsProps = useMemo(() => {
const isListWithSingleTimestampOrder =
requestData.graphType === PANEL_TYPES.LIST &&
requestData.query.builder?.queryData[0]?.orderBy?.length === 1 &&
// exclude list with id filter (i.e. context logs)
!requestData.query.builder?.queryData[0].filters.items.some(
(filter) => filter.key?.key === 'id',
) &&
requestData.query.builder?.queryData[0].orderBy[0].columnName ===
'timestamp';
const modifiedRequestData = {
...requestData,
graphType:
requestData.graphType === PANEL_TYPES.BAR
? PANEL_TYPES.TIME_SERIES
: requestData.graphType,
}),
[requestData],
);
};
// If the query is a list with a single timestamp order, we need to add the id column to the order by clause
if (isListWithSingleTimestampOrder) {
modifiedRequestData.query.builder = {
...requestData.query.builder,
queryData: [
{
...requestData.query.builder.queryData[0],
orderBy: [
...requestData.query.builder.queryData[0].orderBy,
{
columnName: 'id',
order: requestData.query.builder.queryData[0].orderBy[0].order,
},
],
},
],
};
}
return modifiedRequestData;
}, [requestData]);
const queryKey = useMemo(() => {
if (options?.queryKey && Array.isArray(options.queryKey)) {

View File

@@ -5,7 +5,7 @@ import {
PANEL_TYPES,
} from 'constants/queryBuilder';
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import { getPaginationQueryData } from 'lib/newQueryBuilder/getPaginationQueryData';
import { getPaginationQueryDataV2 } from 'lib/newQueryBuilder/getPaginationQueryData';
import { useEffect, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
@@ -100,10 +100,7 @@ export const useLogsData = ({
): Query | null => {
if (!query) return null;
const paginateData = getPaginationQueryData({
filters: params.filters,
listItemId: params.log ? params.log.id : null,
orderByTimestamp,
const paginateData = getPaginationQueryDataV2({
page: params.page,
pageSize: params.pageSize,
});

View File

@@ -8,6 +8,27 @@ import {
} from 'types/api/queryBuilder/queryBuilderData';
import { v4 as uuid } from 'uuid';
type SetupPaginationQueryDataParamsV2 = {
page: number;
pageSize: number;
};
type SetupPaginationQueryDataV2 = (
params: SetupPaginationQueryDataParamsV2,
) => Partial<IBuilderQuery>;
export const getPaginationQueryDataV2: SetupPaginationQueryDataV2 = ({
page,
pageSize,
}) => {
const offset = (page - 1) * pageSize;
return {
offset,
pageSize,
};
};
type SetupPaginationQueryDataParams = {
filters: IBuilderQuery['filters'];
listItemId: string | null;