Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@
},
"dependencies": {
"formik": "^2.2.6",
"react-graph-vis": "^1.0.7"
"react-graph-vis": "^1.0.7",
"chart.js": "^4.4.8",
"chartjs-adapter-moment": "^1.0.1"
}
}
67 changes: 22 additions & 45 deletions public/pages/Alerts/containers/Alerts/Alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,14 @@ import {
EuiToolTip,
EuiEmptyPrompt,
EuiTableSelectionType,
EuiIcon,
EuiTabbedContent,
EuiText,
} from '@elastic/eui';
import { FieldValueSelectionFilterConfigType } from '@elastic/eui/src/components/search_bar/filters/field_value_selection_filter';
import dateMath from '@elastic/datemath';
import React, { Component } from 'react';
import { ContentPanel } from '../../../../components/ContentPanel';
import {
getAlertsVisualizationSpec,
getChartTimeUnit,
getDomainRange,
TimeUnit,
} from '../../../Overview/utils/helpers';
import { getChartTimeUnit, TimeUnit } from '../../../Overview/utils/helpers';
import moment from 'moment';
import {
ALERT_STATE,
Expand Down Expand Up @@ -58,13 +52,11 @@ import {
errorNotificationToast,
getDuration,
renderTime,
renderVisualization,
setBreadcrumbs,
successNotificationToast,
} from '../../../../utils/helpers';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { match, RouteComponentProps, withRouter } from 'react-router-dom';
import { ChartContainer } from '../../../../components/Charts/ChartContainer';
import {
AlertItem,
CorrelationAlertTableItem,
Expand All @@ -77,6 +69,7 @@ import { DurationRange } from '@elastic/eui/src/components/date_picker/types';
import { DataStore } from '../../../../store/DataStore';
import { ThreatIntelAlertsTable } from '../../components/ThreatIntelAlertsTable/ThreatIntelAlertsTable';
import { PageHeader } from '../../../../components/PageHeader/PageHeader';
import { createBarChartWrapper } from '../../../../utils/chartUtils';

type FilterAlertParams =
| { alerts: AlertItem[]; timeField: 'last_notification_time' }
Expand Down Expand Up @@ -121,11 +114,13 @@ export interface AlertsState {
selectedTabId: AlertTabId;
}

const groupByOptions = [
export const alertsGroupByOptions = [
{ text: 'Alert status', value: 'status' },
{ text: 'Alert severity', value: 'severity' },
];

const ALERTS_VIEW_CHART = 'alerts-view';

export class Alerts extends Component<AlertsProps, AlertsState> {
private abortControllers: AbortController[] = [];

Expand Down Expand Up @@ -248,7 +243,7 @@ export class Alerts extends Component<AlertsProps, AlertsState> {
/>
),
});
renderVisualization(this.generateVisualizationSpec(filteredAlerts), 'alerts-view');
this.getChart(this.getVisData(filteredAlerts));
};

filterCorrelationAlerts = () => {
Expand All @@ -272,7 +267,7 @@ export class Alerts extends Component<AlertsProps, AlertsState> {
/>
),
});
renderVisualization(this.generateVisualizationSpec(filteredCorrelationAlerts), 'alerts-view');
this.getChart(this.getVisData(filteredCorrelationAlerts));
};

filterThreatIntelAlerts = () => {
Expand All @@ -296,28 +291,19 @@ export class Alerts extends Component<AlertsProps, AlertsState> {
/>
),
});
renderVisualization(this.generateVisualizationSpec(filteredAlerts), 'alerts-view');
this.getChart(this.getVisData(filteredAlerts));
};

private renderVisAsPerTab() {
switch (this.state.selectedTabId) {
case AlertTabId.DetectionRules:
renderVisualization(
this.generateVisualizationSpec(this.state.filteredAlerts),
'alerts-view'
);
this.getChart(this.getVisData(this.state.filteredAlerts));
break;
case AlertTabId.Correlations:
renderVisualization(
this.generateVisualizationSpec(this.state.filteredCorrelationAlerts),
'alerts-view'
);
this.getChart(this.getVisData(this.state.filteredCorrelationAlerts));
break;
case AlertTabId.ThreatIntel:
renderVisualization(
this.generateVisualizationSpec(this.state.filteredThreatIntelAlerts),
'alerts-view'
);
this.getChart(this.getVisData(this.state.filteredThreatIntelAlerts));
break;
}
}
Expand Down Expand Up @@ -518,39 +504,28 @@ export class Alerts extends Component<AlertsProps, AlertsState> {
this.setState({ flyoutCorrelationData: alertItem ? { alertItem } : undefined });
}

generateVisualizationSpec(alerts: (AlertItem | CorrelationAlertTableItem | ThreatIntelAlert)[]) {
const visData = alerts.map((alert) => {
getChart(data: any) {
createBarChartWrapper(data, this.state.groupBy, ALERTS_VIEW_CHART, this.props.dateTimeFilter);
}

getVisData(alerts: (AlertItem | CorrelationAlertTableItem | ThreatIntelAlert)[]) {
return alerts.map((alert) => {
const time = new Date(alert.start_time);
time.setMilliseconds(0);
time.setSeconds(0);

return {
alert: 1,
time,
time: time.getTime(),
status: alert.state,
severity: parseAlertSeverityToOption(alert.severity)?.label || alert.severity,
};
});
const {
dateTimeFilter = {
startTime: DEFAULT_DATE_RANGE.start,
endTime: DEFAULT_DATE_RANGE.end,
},
} = this.props;
const chartTimeUnits = getChartTimeUnit(dateTimeFilter.startTime, dateTimeFilter.endTime);
return getAlertsVisualizationSpec(visData, this.state.groupBy, {
timeUnit: chartTimeUnits.timeUnit,
dateFormat: chartTimeUnits.dateFormat,
domain: getDomainRange(
[dateTimeFilter.startTime, dateTimeFilter.endTime],
chartTimeUnits.timeUnit.unit
),
});
}

createGroupByControl(): React.ReactNode {
return createSelectComponent(
groupByOptions,
alertsGroupByOptions,
this.state.groupBy,
'alert-vis-groupBy',
(event) => {
Expand Down Expand Up @@ -1181,7 +1156,9 @@ export class Alerts extends Component<AlertsProps, AlertsState> {
}
/>
) : (
<ChartContainer chartViewId={'alerts-view'} loading={loading} />
<div id="chart-container">
<canvas id={ALERTS_VIEW_CHART}></canvas>
</div>
)}
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
66 changes: 33 additions & 33 deletions public/pages/Findings/containers/Findings/Findings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,21 @@ import {
FindingTabId,
MAX_RECENTLY_USED_TIME_RANGES,
} from '../../../../utils/constants';
import {
getChartTimeUnit,
getDomainRange,
getFindingsVisualizationSpec,
getThreatIntelFindingsVisualizationSpec,
TimeUnit,
} from '../../../Overview/utils/helpers';
import { getChartTimeUnit, TimeUnit } from '../../../Overview/utils/helpers';
import {
getNotificationChannels,
parseNotificationChannelsToOptions,
} from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers';
import {
createSelectComponent,
errorNotificationToast,
renderVisualization,
getDuration,
getIsNotificationPluginInstalled,
setBreadcrumbs,
isThreatIntelQuery,
} from '../../../../utils/helpers';
import { RuleSource } from '../../../../../server/models/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { ChartContainer } from '../../../../components/Charts/ChartContainer';
import { DataStore } from '../../../../store/DataStore';
import { DurationRange } from '@elastic/eui/src/components/date_picker/types';
import {
Expand All @@ -69,6 +61,7 @@ import {
import { ThreatIntelFindingsTable } from '../../components/FindingsTable/ThreatIntelFindingsTable';
import { PageHeader } from '../../../../components/PageHeader/PageHeader';
import { RuleSeverityValue, RuleSeverityPriority } from '../../../Rules/utils/constants';
import { createBarChartWrapper } from '../../../../utils/chartUtils';

interface FindingsProps extends RouteComponentProps, DataSourceProps {
detectorService: DetectorsService;
Expand Down Expand Up @@ -133,6 +126,8 @@ export const groupByOptionsByTabId = {
[FindingTabId.ThreatIntel]: [{ text: 'Indicator type', value: 'indicatorType' }],
};

const FINDINGS_VIEW_CHART = 'findings-view';

class Findings extends Component<FindingsProps, FindingsState> {
private abortGetFindingsControllers: AbortController[] = [];

Expand Down Expand Up @@ -219,7 +214,8 @@ class Findings extends Component<FindingsProps, FindingsState> {
) {
this.onRefresh();
} else if (this.shouldUpdateVisualization(prevState)) {
renderVisualization(this.generateVisualizationSpec(), 'findings-view');
const data = this.generateVisualizationData();
this.createStackedBarChart(data.visData, data.groupBy);
}
}

Expand All @@ -239,7 +235,8 @@ class Findings extends Component<FindingsProps, FindingsState> {
} else if (this.state.selectedTabId === FindingTabId.ThreatIntel) {
await this.getThreatIntelFindings();
}
renderVisualization(this.generateVisualizationSpec(), 'findings-view');
const data = this.generateVisualizationData();
this.createStackedBarChart(data.visData, data.groupBy);
};

setStateForTab<T extends FindingTabId, F extends keyof FindingsState['findingStateByTabId'][T]>(
Expand Down Expand Up @@ -419,7 +416,7 @@ class Findings extends Component<FindingsProps, FindingsState> {
});
};

generateVisualizationSpec() {
generateVisualizationData() {
const visData: (FindingVisualizationData | ThreatIntelFindingVisualizationData)[] = [];
const { selectedTabId, findingStateByTabId } = this.state;

Expand All @@ -428,7 +425,6 @@ class Findings extends Component<FindingsProps, FindingsState> {
? findingStateByTabId[FindingTabId.DetectionRules]
: findingStateByTabId[FindingTabId.ThreatIntel];
const groupBy = findingsState.groupBy;
let specGetter;

if (selectedTabId === FindingTabId.DetectionRules) {
(findingsState.filteredFindings as FindingItemType[]).forEach((finding: FindingItemType) => {
Expand All @@ -449,7 +445,6 @@ class Findings extends Component<FindingsProps, FindingsState> {
ruleLevel === 'critical' ? ruleLevel : (finding as any)['ruleSeverity'] || ruleLevel,
});
});
specGetter = getFindingsVisualizationSpec;
} else {
(findingsState.findings as ThreatIntelFinding[]).forEach((finding) => {
const findingTime = new Date(finding.timestamp);
Expand All @@ -462,24 +457,12 @@ class Findings extends Component<FindingsProps, FindingsState> {
indicatorType: finding.ioc_type,
});
});
specGetter = getThreatIntelFindingsVisualizationSpec;
}
const {
dateTimeFilter = {
startTime: DEFAULT_DATE_RANGE.start,
endTime: DEFAULT_DATE_RANGE.end,
},
} = this.props;
const chartTimeUnits = getChartTimeUnit(dateTimeFilter.startTime, dateTimeFilter.endTime);

return specGetter(visData, groupBy, {
timeUnit: chartTimeUnits.timeUnit,
dateFormat: chartTimeUnits.dateFormat,
domain: getDomainRange(
[dateTimeFilter.startTime, dateTimeFilter.endTime],
chartTimeUnits.timeUnit.unit
),
});

return {
visData,
groupBy,
};
}

createGroupByControl(): React.ReactNode {
Expand All @@ -506,6 +489,21 @@ class Findings extends Component<FindingsProps, FindingsState> {
});
};

createStackedBarChart(
data: (FindingVisualizationData | ThreatIntelFindingVisualizationData)[],
groupBy: string
) {
// Calculate the time difference in milliseconds
const {
dateTimeFilter = {
startTime: DEFAULT_DATE_RANGE.start,
endTime: DEFAULT_DATE_RANGE.end,
},
} = this.props;

createBarChartWrapper(data, groupBy, FINDINGS_VIEW_CHART, dateTimeFilter);
}

render() {
const {
loading,
Expand Down Expand Up @@ -541,7 +539,7 @@ class Findings extends Component<FindingsProps, FindingsState> {

finding['ruleName'] =
matchedRules[0]?.title ||
(finding.queries.find(({ id }) => isThreatIntelQuery(id))
(finding.queries.find(({ id }: any) => isThreatIntelQuery(id))
? 'Threat intel'
: DEFAULT_EMPTY_DATA);
finding['ruleSeverity'] =
Expand Down Expand Up @@ -679,7 +677,9 @@ class Findings extends Component<FindingsProps, FindingsState> {
}
/>
) : (
<ChartContainer chartViewId={'findings-view'} loading={loading} />
<div id="chart-container">
<canvas id={FINDINGS_VIEW_CHART}></canvas>
</div>
)}
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Loading