Skip to content

Commit f1e4e26

Browse files
committed
refactor incidents to add typescript
Signed-off-by: Gabriel Bernal <[email protected]>
1 parent 878e1a1 commit f1e4e26

File tree

11 files changed

+211
-228
lines changed

11 files changed

+211
-228
lines changed

web/src/components/Incidents/AlertsChart/AlertsChart.jsx renamed to web/src/components/Incidents/AlertsChart/AlertsChart.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { Card, CardBody, CardTitle, EmptyState, EmptyStateBody } from '@patternf
1414
import { createAlertsChartBars, formatDate, generateDateArray } from '../utils';
1515
import { getResizeObserver } from '@patternfly/react-core';
1616
import { useDispatch, useSelector } from 'react-redux';
17-
import * as _ from 'lodash-es';
1817
import { setAlertsAreLoading } from '../../../actions/observe';
1918
import {
2019
t_global_color_status_danger_default,
@@ -24,8 +23,8 @@ import {
2423

2524
const AlertsChart = ({ chartDays, theme }) => {
2625
const dispatch = useDispatch();
27-
const [chartContainerHeight, setChartContainerHeight] = React.useState();
28-
const [chartHeight, setChartHeight] = React.useState();
26+
const [chartContainerHeight, setChartContainerHeight] = React.useState<number>(0);
27+
const [chartHeight, setChartHeight] = React.useState<number>();
2928
const alertsData = useSelector((state) =>
3029
state.plugins.mcp.getIn(['incidentsData', 'alertsData']),
3130
);
@@ -43,12 +42,12 @@ const AlertsChart = ({ chartDays, theme }) => {
4342

4443
const chartData = React.useMemo(() => {
4544
if (!Array.isArray(alertsData) || alertsData.length === 0) return [];
46-
return alertsData.map((alert) => createAlertsChartBars(alert, theme, dateValues));
47-
}, [alertsData, theme, dateValues]);
45+
return alertsData.map((alert) => createAlertsChartBars(alert, dateValues));
46+
}, [alertsData, dateValues]);
4847

4948
React.useEffect(() => {
50-
setChartContainerHeight(chartData?.length < 5 ? 300 : chartData?.length * 60);
51-
setChartHeight(chartData?.length < 5 ? 250 : chartData?.length * 55);
49+
setChartContainerHeight(chartData ? (chartData?.length < 5 ? 300 : chartData?.length * 60) : 0);
50+
setChartHeight(chartData ? (chartData?.length < 5 ? 250 : chartData?.length * 55) : 0);
5251
}, [chartData]);
5352

5453
const selectedIncidentIsVisible = React.useMemo(() => {
@@ -78,7 +77,7 @@ const AlertsChart = ({ chartDays, theme }) => {
7877
<CardTitle>Alerts Timeline</CardTitle>
7978
{alertsAreLoading ? (
8079
<EmptyState
81-
variant="large"
80+
variant="lg"
8281
style={{
8382
height: '250px',
8483
}}
@@ -88,7 +87,7 @@ const AlertsChart = ({ chartDays, theme }) => {
8887
) : (
8988
<CardBody
9089
style={{
91-
height: { chartContainerHeight },
90+
height: chartContainerHeight,
9291
width: '100%',
9392
}}
9493
>
@@ -98,7 +97,7 @@ const AlertsChart = ({ chartDays, theme }) => {
9897
labelComponent={
9998
<ChartTooltip
10099
orientation="top"
101-
dx={({ x, x0 }) => -(x - x0) / 2}
100+
dx={({ x, datum }) => -Math.abs(x - datum.x) / 2}
102101
dy={-5} // Position tooltip so pointer appears above bar
103102
constrainToVisibleArea
104103
labelComponent={<ChartLabel />}

web/src/components/Incidents/IncidentsChart/IncidentsChart.jsx renamed to web/src/components/Incidents/IncidentsChart/IncidentsChart.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,22 @@ import { setAlertsAreLoading } from '../../../actions/observe';
3131
const IncidentsChart = ({ incidentsData, chartDays, theme }) => {
3232
const dispatch = useDispatch();
3333
const [isLoading, setIsLoading] = React.useState(true);
34-
const [chartContainerHeight, setChartContainerHeight] = React.useState();
35-
const [chartHeight, setChartHeight] = React.useState();
34+
const [chartContainerHeight, setChartContainerHeight] = React.useState<number>(0);
35+
const [chartHeight, setChartHeight] = React.useState<number>();
3636
const dateValues = React.useMemo(() => generateDateArray(chartDays), [chartDays]);
3737

3838
const chartData = React.useMemo(() => {
3939
if (!Array.isArray(incidentsData) || incidentsData.length === 0) return [];
40-
return incidentsData.map((incident) => createIncidentsChartBars(incident, theme, dateValues));
41-
}, [incidentsData, theme, dateValues]);
40+
return incidentsData.map((incident) => createIncidentsChartBars(incident, dateValues));
41+
}, [incidentsData, dateValues]);
4242

4343
React.useEffect(() => {
4444
setIsLoading(false);
4545
}, [incidentsData]);
4646

4747
React.useEffect(() => {
48-
setChartContainerHeight(chartData?.length < 5 ? 300 : chartData?.length * 60);
49-
setChartHeight(chartData?.length < 5 ? 250 : chartData?.length * 55);
48+
setChartContainerHeight(chartData ? (chartData?.length < 5 ? 300 : chartData?.length * 60) : 0);
49+
setChartHeight(chartData ? (chartData?.length < 5 ? 250 : chartData?.length * 55) : 0);
5050
}, [chartData]);
5151

5252
const [width, setWidth] = React.useState(0);
@@ -107,7 +107,7 @@ const IncidentsChart = ({ incidentsData, chartDays, theme }) => {
107107
) : (
108108
<CardBody
109109
style={{
110-
height: { chartContainerHeight },
110+
height: chartContainerHeight,
111111
width: '100%',
112112
}}
113113
>
@@ -117,7 +117,7 @@ const IncidentsChart = ({ incidentsData, chartDays, theme }) => {
117117
labelComponent={
118118
<ChartTooltip
119119
orientation="top"
120-
dx={({ x, x0 }) => -(x - x0) / 2}
120+
dx={({ x, datum }) => -Math.abs(x - datum.x) / 2}
121121
dy={-5} // Position tooltip so pointer appears above bar
122122
constrainToVisibleArea
123123
labelComponent={<ChartLabel />}
@@ -192,7 +192,7 @@ const IncidentsChart = ({ incidentsData, chartDays, theme }) => {
192192
//we have several arrays and for each array we make a ChartBar
193193
<ChartBar
194194
data={bar}
195-
key={bar.group_id}
195+
key={bar[0].group_id}
196196
style={{
197197
data: {
198198
fill: ({ datum }) => datum.fill,
@@ -203,6 +203,7 @@ const IncidentsChart = ({ incidentsData, chartDays, theme }) => {
203203
}}
204204
events={[
205205
{
206+
target: 'data',
206207
eventHandlers: {
207208
onClick: (props, datum) => clickHandler(props, datum),
208209
},

web/src/components/Incidents/IncidentsDetailsRowTable.jsx renamed to web/src/components/Incidents/IncidentsDetailsRowTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const IncidentsDetailsRowTable = ({ alerts }) => {
6767
}, [memoizedAlerts, alertsWithLabels]);
6868

6969
return (
70-
<Table borders={'compactBorderless'}>
70+
<Table borders={false} variant="compact">
7171
<Thead>
7272
<Tr>
7373
<Th width={25}>{t('Alert Name')}</Th>

web/src/components/Incidents/IncidentsPage.jsx renamed to web/src/components/Incidents/IncidentsPage.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import withFallback from '../console/console-shared/error/fallbacks/withFallback
5656
import IncidentsChart from './IncidentsChart/IncidentsChart';
5757
import AlertsChart from './AlertsChart/AlertsChart';
5858
import { usePatternFlyTheme } from '../hooks/usePatternflyTheme';
59+
import { DaysFilters, IncidentFilters } from './models';
5960

6061
const IncidentsPage = () => {
6162
const { t } = useTranslation(process.env.I18N_NAMESPACE);
@@ -69,7 +70,7 @@ const IncidentsPage = () => {
6970
// days span is where we store the value for creating time ranges for
7071
// fetch incidents/alerts based on the length of time ranges
7172
// when days filter changes we set a new days span -> calculate new time range and fetch new data
72-
const [daysSpan, setDaysSpan] = React.useState();
73+
const [daysSpan, setDaysSpan] = React.useState<number>(7);
7374
const [timeRanges, setTimeRanges] = React.useState([]);
7475
// data that is used for processing to serve it to the alerts table and chart
7576
const [incidentForAlertProcessing, setIncidentForAlertProcessing] = React.useState([]);
@@ -271,9 +272,9 @@ const IncidentsPage = () => {
271272
}
272273
}, [incidentGroupId, timeRanges]);
273274

274-
const onSelect = (_event, value) => {
275+
const onSelect = (_event: React.MouseEvent, value?: string) => {
275276
if (value) {
276-
changeDaysFilter(value, dispatch, incidentsActiveFilters);
277+
changeDaysFilter(value as DaysFilters, dispatch, incidentsActiveFilters);
277278
}
278279

279280
setDaysFilterIsExpanded(false);
@@ -297,15 +298,20 @@ const IncidentsPage = () => {
297298
id="toolbar-with-filter"
298299
collapseListedFiltersBreakpoint="xl"
299300
clearAllFilters={() =>
300-
onDeleteIncidentFilterChip('', '', incidentsActiveFilters, dispatch)
301+
onDeleteIncidentFilterChip('', undefined, incidentsActiveFilters, dispatch)
301302
}
302303
>
303304
<ToolbarContent>
304305
<ToolbarItem>
305306
<ToolbarFilter
306307
labels={incidentsActiveFilters.incidentFilters}
307308
deleteLabel={(category, chip) =>
308-
onDeleteIncidentFilterChip(category, chip, incidentsActiveFilters, dispatch)
309+
onDeleteIncidentFilterChip(
310+
category as string,
311+
chip as IncidentFilters,
312+
incidentsActiveFilters,
313+
dispatch,
314+
)
309315
}
310316
deleteLabelGroup={() =>
311317
onDeleteGroupIncidentFilterChip(incidentsActiveFilters, dispatch)
@@ -319,7 +325,12 @@ const IncidentsPage = () => {
319325
isOpen={incidentFilterIsExpanded}
320326
selected={incidentsActiveFilters.incidentFilters}
321327
onSelect={(event, selection) =>
322-
onIncidentFiltersSelect(event, selection, dispatch, incidentsActiveFilters)
328+
onIncidentFiltersSelect(
329+
event,
330+
selection as IncidentFilters,
331+
dispatch,
332+
incidentsActiveFilters,
333+
)
323334
}
324335
onOpenChange={(isOpen) => setIncidentIsExpanded(isOpen)}
325336
toggle={(toggleRef) => (

web/src/components/Incidents/IncidentsTable.jsx renamed to web/src/components/Incidents/IncidentsTable.tsx

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1+
import { AlertSeverity, AlertStates } from '@openshift-console/dynamic-plugin-sdk';
2+
import { Bullseye, Card, CardBody, EmptyState, EmptyStateBody } from '@patternfly/react-core';
3+
import { SearchIcon } from '@patternfly/react-icons';
4+
import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table';
5+
import * as _ from 'lodash-es';
16
import React from 'react';
2-
import { Table, Thead, Tr, Th, Tbody, Td, ExpandableRowContent } from '@patternfly/react-table';
3-
import {
4-
Bullseye,
5-
Card,
6-
CardBody,
7-
EmptyState,
8-
EmptyStateBody,
9-
Label,
10-
} from '@patternfly/react-core';
11-
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';
12-
import IncidentsDetailsRowTable from './IncidentsDetailsRowTable';
13-
import { BellIcon, BellSlashIcon, SearchIcon } from '@patternfly/react-icons';
147
import { useSelector } from 'react-redux';
15-
import * as _ from 'lodash-es';
16-
import { AlertState, AlertStateIcon, SeverityBadge } from '../alerting/AlertUtils';
17-
import { AlertSeverity, AlertStates } from '@openshift-console/dynamic-plugin-sdk';
8+
import { AlertStateIcon, SeverityBadge } from '../alerting/AlertUtils';
9+
import IncidentsDetailsRowTable from './IncidentsDetailsRowTable';
1810

19-
export const IncidentsTable = ({ namespace }) => {
11+
export const IncidentsTable = () => {
2012
const columnNames = {
2113
checkbox: '',
2214
component: 'Component',
@@ -108,10 +100,7 @@ export const IncidentsTable = ({ namespace }) => {
108100
<Tr isExpanded={isAlertExpanded(alert)}>
109101
<Td width={100} colSpan={6}>
110102
<ExpandableRowContent>
111-
<IncidentsDetailsRowTable
112-
alerts={alert.alertsExpandedRowData}
113-
namespace={namespace}
114-
/>
103+
<IncidentsDetailsRowTable alerts={alert.alertsExpandedRowData} />
115104
</ExpandableRowContent>
116105
</Td>
117106
</Tr>
File renamed without changes.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
export type Timestamps = [Date, string];
2+
3+
export type SpanDates = Array<Date>;
4+
5+
export type AlertsIntervalsArray = [Date, Date, 'data' | 'nodata'];
6+
7+
export type Incident = {
8+
component: string;
9+
componentList: Array<string>;
10+
layer: string;
11+
firing: boolean;
12+
group_id: string;
13+
src_severity: string;
14+
src_alertname: string;
15+
src_namespace: string;
16+
x: number;
17+
values: Array<Timestamps>;
18+
metric: Metric;
19+
};
20+
21+
// Define the interface for Metric
22+
export type Metric = {
23+
group_id: string; // The unique ID for grouping
24+
component: string; // Component name
25+
componentList?: string[]; // List of all unique components
26+
[key: string]: any; // Allow other dynamic fields in Metric
27+
};
28+
29+
export type ProcessedIncident = Omit<Incident, 'metric'> & {
30+
informative: boolean;
31+
critical: boolean;
32+
warning: boolean;
33+
resolved: boolean;
34+
firing: boolean;
35+
};
36+
37+
export type Alert = {
38+
alertname: string;
39+
alertsStartFiring: Date;
40+
alertsEndFiring: Date;
41+
alertstate: string;
42+
component: string;
43+
layer: string;
44+
name: string;
45+
namespace: string;
46+
resolved: boolean;
47+
severity: 'critical' | 'warning' | 'info';
48+
x: number;
49+
values: Array<Timestamps>;
50+
};
51+
52+
export type DaysFilters = '1 day' | '3 days' | '7 days' | '15 days';
53+
54+
export type IncidentFilters = 'Critical' | 'Warning' | 'Firing' | 'Informative' | 'Resolved';
55+
56+
export type IncidentFiltersCombined = {
57+
days: Array<DaysFilters>;
58+
incidentFilters: Array<IncidentFilters>;
59+
groupId?: Array<string>;
60+
};

web/src/components/Incidents/processAlerts.js renamed to web/src/components/Incidents/processAlerts.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,13 @@
11
/* eslint-disable max-len */
22

3-
import { sortObjectsByEarliestTimestamp } from './processIncidents';
3+
import { Incident } from './models';
4+
import { sortObjectsByEarliestTimestamp } from './utils';
45

56
/**
67
* Groups alert objects by their `alertname`, `namespace`, and `component` fields and merges their values
78
* while removing duplicates. Alerts with the same combination of `alertname`, `namespace`, and `component`
89
* are combined, with values being deduplicated.
910
*
10-
* @param {Array<Object>} objects - Array of alert objects to be grouped. Each object contains a `metric` field
11-
* with properties such as `alertname`, `namespace`, `component`, and an array of `values`.
12-
* @param {Object} objects[].metric - The metric information of the alert.
13-
* @param {string} objects[].metric.alertname - The name of the alert.
14-
* @param {string} objects[].metric.namespace - The namespace in which the alert is raised.
15-
* @param {string} objects[].metric.component - The component associated with the alert.
16-
* @param {Array<Array<Number | string>>} objects[].values - The array of values corresponding to the alert, where
17-
* each value is a tuple containing a timestamp and a value (e.g., [timestamp, value]).
18-
*
19-
* @returns {Array<Object>} - An array of grouped alert objects. Each object contains a unique combination of
20-
* `alertname`, `namespace`, and `component`, with deduplicated values.
21-
* @returns {Object} return[].metric - The metric information of the grouped alert.
22-
* @returns {Array<Array<Number | string>>} return[].values - The deduplicated array of values for the grouped alert.
23-
*
2411
* @example
2512
* const alerts = [
2613
* { metric: { alertname: "Alert1", namespace: "ns1", component: "comp1" }, values: [[12345, "2"], [12346, "2"]] },
@@ -29,7 +16,7 @@ import { sortObjectsByEarliestTimestamp } from './processIncidents';
2916
* const groupedAlerts = groupAlerts(alerts);
3017
* // Returns an array where the two alerts are grouped together with deduplicated values.
3118
*/
32-
export function groupAlerts(objects) {
19+
export function groupAlerts(objects: Array<Incident>) {
3320
// Step 1: Filter out all non firing alerts
3421
const filteredObjects = objects.filter((obj) => obj.metric.alertstate === 'firing');
3522
const groupedObjects = new Map();
@@ -137,7 +124,7 @@ export function groupAlerts(objects) {
137124
* // ]
138125
*/
139126

140-
export function processAlerts(data, selectedIncidents) {
127+
export function processAlerts(data: Array<Incident>, selectedIncidents) {
141128
const firing = groupAlerts(data).filter((alert) => alert.metric.alertname !== 'Watchdog');
142129

143130
// Extract the first and last timestamps from selectedIncidents
@@ -152,16 +139,16 @@ export function processAlerts(data, selectedIncidents) {
152139
// Filter values based on firstTimestamp and lastTimestamp keep only values within range
153140
const processedValues = alert.values
154141
.map((value) => {
155-
const timestamp = new Date(value[0] * 1000);
142+
const timestamp = new Date(value[0].getTime() * 1000);
156143
return [timestamp, value[1]];
157144
})
158-
.filter(([date]) => date >= firstTimestamp && date <= lastTimestamp);
145+
.filter(([date]) => date >= firstTimestamp && date <= lastTimestamp) as Array<[Date, string]>;
159146

160-
const sortedValues = processedValues.sort((a, b) => a[0] - b[0]);
147+
const sortedValues = processedValues.sort((a, b) => a[0].getTime() - b[0].getTime());
161148

162149
const alertsStartFiring = sortedValues[0][0];
163150
const alertsEndFiring = sortedValues[sortedValues.length - 1][0];
164-
const resolved = new Date() - alertsEndFiring > 10 * 60 * 1000;
151+
const resolved = Date.now() - alertsEndFiring.getTime() > 10 * 60 * 1000;
165152

166153
return {
167154
alertname: alert.metric.alertname,

0 commit comments

Comments
 (0)