/* eslint-disable no-nested-ternary */ import './NodeEvents.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Table, TableColumnsType } from 'antd'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { EventContents } from 'container/InfraMonitoringK8s/commonUtils'; import LoadingContainer from 'container/InfraMonitoringK8s/LoadingContainer'; import LogsError from 'container/LogsError/LogsError'; import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config'; import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch'; import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2'; import { CustomTimeType, Time, } from 'container/TopNav/DateTimeSelectionV2/config'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults'; 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 { getNodesEventsQueryPayload } from './constants'; import NoEventsContainer from './NoEventsContainer'; interface EventDataType { key: string; timestamp: string; body: string; id: string; attributes_bool?: Record; attributes_number?: Record; attributes_string?: Record; resources_string?: Record; scope_name?: string; scope_string?: Record; scope_version?: string; severity_number?: number; severity_text?: string; span_id?: string; trace_flags?: number; trace_id?: string; severity?: string; } interface INodeEventsProps { timeRange: { startTime: number; endTime: number; }; handleChangeEventFilters: (filters: IBuilderQuery['filters']) => void; filters: IBuilderQuery['filters']; isModalTimeSelection: boolean; handleTimeChange: ( interval: Time | CustomTimeType, dateTimeRange?: [number, number], ) => void; selectedInterval: Time; } const EventsPageSize = 10; export default function Events({ timeRange, handleChangeEventFilters, filters, isModalTimeSelection, handleTimeChange, selectedInterval, }: INodeEventsProps): JSX.Element { const { currentQuery } = useQueryBuilder(); const [formattedNodeEvents, setFormattedNodeEvents] = useState< EventDataType[] >([]); const [hasReachedEndOfEvents, setHasReachedEndOfEvents] = useState(false); const [page, setPage] = useState(1); const updatedCurrentQuery = useMemo( () => ({ ...currentQuery, builder: { ...currentQuery.builder, queryData: [ { ...currentQuery.builder.queryData[0], dataSource: DataSource.LOGS, aggregateOperator: 'noop', aggregateAttribute: { ...currentQuery.builder.queryData[0].aggregateAttribute, }, filters: { items: [], op: 'AND', }, }, ], }, }), [currentQuery], ); const query = updatedCurrentQuery?.builder?.queryData[0] || null; const queryPayload = useMemo(() => { const basePayload = getNodesEventsQueryPayload( timeRange.startTime, timeRange.endTime, filters, ); basePayload.query.builder.queryData[0].pageSize = 10; basePayload.query.builder.queryData[0].orderBy = [ { columnName: 'timestamp', order: ORDERBY_FILTERS.DESC }, ]; return basePayload; }, [timeRange.startTime, timeRange.endTime, filters]); const { data: eventsData, isLoading, isFetching, isError } = useQuery({ queryKey: ['nodeEvents', timeRange.startTime, timeRange.endTime, filters], queryFn: () => GetMetricQueryRange(queryPayload, DEFAULT_ENTITY_VERSION), enabled: !!queryPayload, }); const columns: TableColumnsType = [ { title: 'Severity', dataIndex: 'severity', key: 'severity', width: 100 }, { title: 'Timestamp', dataIndex: 'timestamp', width: 200, ellipsis: true, key: 'timestamp', }, { title: 'Body', dataIndex: 'body', key: 'body' }, ]; useEffect(() => { if (eventsData?.payload?.data?.newResult?.data?.result) { const responsePayload = eventsData?.payload.data.newResult.data.result[0].list || []; const formattedData = responsePayload?.map( (event): EventDataType => ({ timestamp: event.timestamp, severity: event.data.severity_text, body: event.data.body, id: event.data.id, key: event.data.id, resources_string: event.data.resources_string, }), ); setFormattedNodeEvents(formattedData); if ( !responsePayload || (responsePayload && isArray(responsePayload) && responsePayload.length < EventsPageSize) ) { setHasReachedEndOfEvents(true); } else { setHasReachedEndOfEvents(false); } } }, [eventsData]); const handleExpandRow = (record: EventDataType): JSX.Element => ( ); const handlePrev = (): void => { if (!formattedNodeEvents.length) return; setPage(page - 1); const firstEvent = formattedNodeEvents[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 (!formattedNodeEvents.length) return; setPage(page + 1); const lastEvent = formattedNodeEvents[formattedNodeEvents.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 = ({ expanded, onExpand, record, }: { expanded: boolean; onExpand: ( record: EventDataType, e: React.MouseEvent, ) => void; record: EventDataType; }): JSX.Element => expanded ? ( onExpand( record, (e as unknown) as React.MouseEvent, ) } /> ) : ( onExpand( record, (e as unknown) as React.MouseEvent, ) } /> ); return (
{query && ( )}
{isLoading && } {!isLoading && !isError && formattedNodeEvents.length === 0 && ( )} {isError && !isLoading && } {!isLoading && !isError && formattedNodeEvents.length > 0 && (
loading={isLoading && page > 1} columns={columns} expandable={{ expandedRowRender: handleExpandRow, rowExpandable: (record): boolean => record.body !== 'Not Expandable', expandIcon: handleExpandRowIcon, }} dataSource={formattedNodeEvents} pagination={false} rowKey={(record): string => record.id} />
)} {!isError && formattedNodeEvents.length > 0 && (
{(isFetching || isLoading) && ( )}
)}
); }