Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions web/src/components/Incidents/AlertsChart/AlertsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
generateDateArray,
generateAlertsDateArray,
getCurrentTime,
isInTimeWindow,
} from '../utils';
import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime';
import { useTranslation } from 'react-i18next';
Expand All @@ -41,7 +42,7 @@ import { MonitoringState } from '../../../store/store';
import { isEmpty } from 'lodash-es';
import { DataTestIDs } from '../../data-test';

const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => {
const AlertsChart = ({ theme, daysSpan }: { theme: 'light' | 'dark'; daysSpan: number }) => {
const dispatch = useDispatch();
const [chartContainerHeight, setChartContainerHeight] = useState<number>();
const [chartHeight, setChartHeight] = useState<number>();
Expand Down Expand Up @@ -72,8 +73,13 @@ const AlertsChart = ({ theme }: { theme: 'light' | 'dark' }) => {

const chartData: AlertsChartBar[][] = useMemo(() => {
if (!Array.isArray(alertsData) || alertsData.length === 0) return [];
return alertsData.map((alert) => createAlertsChartBars(alert));
}, [alertsData]);
return alertsData
.filter((alert) => {
const lastTimestamp = alert.values[alert.values.length - 1][0];
return isInTimeWindow(lastTimestamp, daysSpan, currentTime);
})
.map((alert) => createAlertsChartBars(alert));
}, [alertsData, currentTime, daysSpan]);

useEffect(() => {
setChartContainerHeight(chartData?.length < 5 ? 300 : chartData?.length * 55);
Expand Down
10 changes: 8 additions & 2 deletions web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
calculateIncidentsChartDomain,
createIncidentsChartBars,
generateDateArray,
isInTimeWindow,
} from '../utils';
import { dateTimeFormatter, timeFormatter } from '../../console/utils/datetime';
import { useTranslation } from 'react-i18next';
Expand Down Expand Up @@ -84,10 +85,15 @@ const IncidentsChart = ({
const chartData = useMemo(() => {
if (!Array.isArray(incidentsData) || incidentsData.length === 0) return [];

const filteredIncidents = selectedGroupId
const groupFilteredIncidents = selectedGroupId
? incidentsData.filter((incident) => incident.group_id === selectedGroupId)
: incidentsData;

const filteredIncidents = groupFilteredIncidents.filter((incident) => {
const lastTimestamp = incident.values[incident.values.length - 1][0];
return isInTimeWindow(lastTimestamp, chartDays * (60 * 60 * 24 * 1000), currentTime);
});

// Create chart bars and sort by original x values to maintain proper order
const chartBars = filteredIncidents.map((incident) =>
createIncidentsChartBars(incident, dateValues),
Expand All @@ -96,7 +102,7 @@ const IncidentsChart = ({

// Reassign consecutive x values to eliminate gaps between bars
return chartBars.map((bars, index) => bars.map((bar) => ({ ...bar, x: index + 1 })));
}, [incidentsData, dateValues, selectedGroupId]);
}, [incidentsData, dateValues, selectedGroupId, currentTime, chartDays]);

useEffect(() => {
setIsLoading(false);
Expand Down
11 changes: 8 additions & 3 deletions web/src/components/Incidents/IncidentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,12 @@ const IncidentsPage = () => {
if (rules && alertsData) {
dispatch(
setAlertsTableData({
alertsTableData: groupAlertsForTable(alertsData, rules),
alertsTableData: groupAlertsForTable(
alertsData,
rules,
incidentsLastRefreshTime,
daysSpan,
),
}),
);
}
Expand Down Expand Up @@ -604,7 +609,7 @@ const IncidentsPage = () => {
<StackItem>
<IncidentsChart
incidentsData={filteredData}
chartDays={timeRanges.length}
chartDays={daysSpan / (60 * 60 * 24 * 1000)} // Convert ms to days
theme={theme}
selectedGroupId={selectedGroupId}
onIncidentClick={handleIncidentChartClick}
Expand All @@ -613,7 +618,7 @@ const IncidentsPage = () => {
/>
</StackItem>
<StackItem>
<AlertsChart theme={theme} />
<AlertsChart theme={theme} daysSpan={daysSpan} />
</StackItem>
</>
)}
Expand Down
13 changes: 12 additions & 1 deletion web/src/components/Incidents/processAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,19 @@ export function convertToAlerts(
export const groupAlertsForTable = (
alerts: Array<Alert>,
alertingRulesData: Array<PrometheusRule>,
currentTime: number,
daysSpan: number,
): Array<GroupedAlert> => {
const groupedAlerts = alerts.reduce((acc: Array<GroupedAlert>, alert) => {
const filteredAlerts = alerts.filter((alert) => {
const endTime = alert.values[alert.values.length - 1][0];
const delta = (currentTime - daysSpan) / 1000;
if (endTime < delta) {
return false;
}
return true;
});

const groupedAlerts = filteredAlerts.reduce((acc: Array<GroupedAlert>, alert) => {
const { component, alertstate, severity, layer, alertname, silenced } = alert;
const existingGroup = acc.find((group) => group.component === component);
const rule = alertingRulesData?.find((rule) => alertname === rule.name);
Expand Down
18 changes: 0 additions & 18 deletions web/src/components/Incidents/processIncidents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,24 +607,6 @@ describe('getIncidentsTimeRanges', () => {
const now = getCurrentTime();

describe('basic functionality', () => {
it('should return single range for timespan less than one day', () => {
const timespan = 12 * 60 * 60 * 1000; // 12 hours
const result = getIncidentsTimeRanges(timespan, now);

expect(result).toHaveLength(1);
expect(result[0].duration).toBe(ONE_DAY);
});

it('should split longer timespans into daily chunks', () => {
const timespan = 3 * ONE_DAY; // 3 days
const result = getIncidentsTimeRanges(timespan, now);

expect(result.length).toBeGreaterThan(1);
result.forEach((range) => {
expect(range.duration).toBe(ONE_DAY);
});
});

it('should use provided maxEndTime', () => {
const maxEndTime = new Date('2024-01-01T00:00:00Z').getTime();
const timespan = ONE_DAY;
Expand Down
12 changes: 2 additions & 10 deletions web/src/components/Incidents/processIncidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,16 +167,8 @@ export const getIncidentsTimeRanges = (
currentTime: number,
): Array<{ endTime: number; duration: number }> => {
const ONE_DAY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const startTime = currentTime - timespan;
const timeRanges = [{ endTime: startTime + ONE_DAY, duration: ONE_DAY }];

while (timeRanges[timeRanges.length - 1].endTime < currentTime) {
const lastRange = timeRanges[timeRanges.length - 1];
const nextEndTime = lastRange.endTime + ONE_DAY;
timeRanges.push({ endTime: nextEndTime, duration: ONE_DAY });
}

return timeRanges;
const FIFTEEN_DAYS = 15 * ONE_DAY;
return [{ endTime: currentTime, duration: FIFTEEN_DAYS }];
};

/**
Expand Down
18 changes: 18 additions & 0 deletions web/src/components/Incidents/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ export const isResolved = (lastTimestamp: number, currentTime: number): boolean
return delta >= threshold;
};

/**
* Checks if the last timestamp is in the time window.
* @param lastTimestamp - The last timestamp in the incident/alert (in seconds)
* @param daysSpan - The number of days in the time window (in milliseconds).
* @param currentTime - The current time in milliseconds.
* @returns true if the last timestamp is in the time window, false otherwise.
*/
export const isInTimeWindow = (
lastTimestamp: number,
daysSpan: number,
currentTime: number,
): boolean => {
// convert chartDays to ms and then convert the result to seconds
const timeWindow = (currentTime - daysSpan) / 1000;
// if endTime is lower than currentTime-chartDays, return false else true
return lastTimestamp >= timeWindow;
};

/**
* Inserts padding data points to ensure the chart renders correctly.
* This allows the chart to properly render events, especially single data points.
Expand Down