diff --git a/package.json b/package.json
index 2f2e25bd5..97362170d 100644
--- a/package.json
+++ b/package.json
@@ -78,13 +78,16 @@
"jest-environment-jsdom": "^27.5.1",
"lint-staged": "^10.2.0",
"string.prototype.replaceall": "1.0.7",
- "ts-loader": "^6.2.1"
+ "ts-loader": "^6.2.1",
+ "@types/chart.js": "^2.9.37"
},
"engines": {
"yarn": "^1.21.1"
},
"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"
}
}
diff --git a/public/pages/Alerts/containers/Alerts/Alerts.tsx b/public/pages/Alerts/containers/Alerts/Alerts.tsx
index ca1e26cc7..1859e96c4 100644
--- a/public/pages/Alerts/containers/Alerts/Alerts.tsx
+++ b/public/pages/Alerts/containers/Alerts/Alerts.tsx
@@ -17,7 +17,6 @@ import {
EuiToolTip,
EuiEmptyPrompt,
EuiTableSelectionType,
- EuiIcon,
EuiTabbedContent,
EuiText,
} from '@elastic/eui';
@@ -25,12 +24,7 @@ import { FieldValueSelectionFilterConfigType } from '@elastic/eui/src/components
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,
@@ -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,
@@ -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' }
@@ -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 {
private abortControllers: AbortController[] = [];
@@ -248,7 +243,7 @@ export class Alerts extends Component {
/>
),
});
- renderVisualization(this.generateVisualizationSpec(filteredAlerts), 'alerts-view');
+ // this.getChart(this.getVisData(filteredAlerts));
};
filterCorrelationAlerts = () => {
@@ -272,7 +267,7 @@ export class Alerts extends Component {
/>
),
});
- renderVisualization(this.generateVisualizationSpec(filteredCorrelationAlerts), 'alerts-view');
+ // this.getChart(this.getVisData(filteredCorrelationAlerts));
};
filterThreatIntelAlerts = () => {
@@ -296,28 +291,19 @@ export class Alerts extends Component {
/>
),
});
- 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;
}
}
@@ -518,39 +504,28 @@ export class Alerts extends Component {
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) => {
@@ -1024,8 +999,8 @@ export class Alerts extends Component {
content: (
<>
- {this.getAlertsGraph(alerts, loading)}
-
+ {/*{this.getAlertsGraph(alerts, loading)}*/}
+ {/**/}
{
content: (
<>
- {this.getAlertsGraph(alerts, loading)}
-
+ {/*{this.getAlertsGraph(alerts, loading)}*/}
+ {/**/}
{
content: (
<>
- {this.getAlertsGraph(alerts, loading)}
-
+ {/*{this.getAlertsGraph(alerts, loading)}*/}
+ {/**/}
{
}
/>
) : (
-
+
+
+
)}
diff --git a/public/pages/Alerts/containers/Alerts/__snapshots__/Alerts.test.tsx.snap b/public/pages/Alerts/containers/Alerts/__snapshots__/Alerts.test.tsx.snap
index 8c07a5059..b4651f427 100644
--- a/public/pages/Alerts/containers/Alerts/__snapshots__/Alerts.test.tsx.snap
+++ b/public/pages/Alerts/containers/Alerts/__snapshots__/Alerts.test.tsx.snap
@@ -835,79 +835,6 @@ exports[` spec renders the component 1`] = `
initialSelectedTab={
Object {
"content":
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
- detectors
-
- to generate alerts.
-
-
- }
- title={
-
-
- No alerts
-
-
- }
- />
-
-
-
@@ -1039,79 +966,6 @@ exports[` spec renders the component 1`] = `
Array [
Object {
"content":
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
- detectors
-
- to generate alerts.
-
-
- }
- title={
-
-
- No alerts
-
-
- }
- />
-
-
-
@@ -1238,79 +1092,6 @@ exports[` spec renders the component 1`] = `
},
Object {
"content":
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
- detectors
-
- to generate alerts.
-
-
- }
- title={
-
-
- No alerts
-
-
- }
- />
-
-
-
@@ -1340,79 +1121,6 @@ exports[` spec renders the component 1`] = `
},
Object {
"content":
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
- detectors
-
- to generate alerts.
-
-
- }
- title={
-
-
- No alerts
-
-
- }
- />
-
-
-
@@ -1670,286 +1378,6 @@ exports[` spec renders the component 1`] = `
id="some_html_id"
role="tabpanel"
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- EuiIconMock
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
- detectors
-
- to generate alerts.
-
-
- }
- title={
-
-
- No alerts
-
-
- }
- >
-
-
-
-
-
- No alerts
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Adjust the time range to see more results or create alert triggers in your
-
-
-
- detectors
-
-
- to generate alerts.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/pages/Findings/containers/Findings/Findings.tsx b/public/pages/Findings/containers/Findings/Findings.tsx
index aeebd7d3e..9a4937b3a 100644
--- a/public/pages/Findings/containers/Findings/Findings.tsx
+++ b/public/pages/Findings/containers/Findings/Findings.tsx
@@ -32,13 +32,7 @@ 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,
@@ -46,7 +40,6 @@ import {
import {
createSelectComponent,
errorNotificationToast,
- renderVisualization,
getDuration,
getIsNotificationPluginInstalled,
setBreadcrumbs,
@@ -54,7 +47,6 @@ import {
} 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 {
@@ -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;
@@ -133,6 +126,8 @@ export const groupByOptionsByTabId = {
[FindingTabId.ThreatIntel]: [{ text: 'Indicator type', value: 'indicatorType' }],
};
+const FINDINGS_VIEW_CHART = 'findings-view';
+
class Findings extends Component {
private abortGetFindingsControllers: AbortController[] = [];
@@ -218,9 +213,11 @@ class Findings extends Component {
this.state.selectedTabId !== prevState.selectedTabId
) {
this.onRefresh();
- } else if (this.shouldUpdateVisualization(prevState)) {
- renderVisualization(this.generateVisualizationSpec(), 'findings-view');
}
+ // else if (this.shouldUpdateVisualization(prevState)) {
+ // const data = this.generateVisualizationData();
+ // this.createStackedBarChart(data.visData, data.groupBy);
+ // }
}
componentDidMount = async () => {
@@ -239,7 +236,8 @@ class Findings extends Component {
} 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(
@@ -419,7 +417,7 @@ class Findings extends Component {
});
};
- generateVisualizationSpec() {
+ generateVisualizationData() {
const visData: (FindingVisualizationData | ThreatIntelFindingVisualizationData)[] = [];
const { selectedTabId, findingStateByTabId } = this.state;
@@ -428,7 +426,6 @@ class Findings extends Component {
? findingStateByTabId[FindingTabId.DetectionRules]
: findingStateByTabId[FindingTabId.ThreatIntel];
const groupBy = findingsState.groupBy;
- let specGetter;
if (selectedTabId === FindingTabId.DetectionRules) {
(findingsState.filteredFindings as FindingItemType[]).forEach((finding: FindingItemType) => {
@@ -449,7 +446,6 @@ class Findings extends Component {
ruleLevel === 'critical' ? ruleLevel : (finding as any)['ruleSeverity'] || ruleLevel,
});
});
- specGetter = getFindingsVisualizationSpec;
} else {
(findingsState.findings as ThreatIntelFinding[]).forEach((finding) => {
const findingTime = new Date(finding.timestamp);
@@ -462,24 +458,12 @@ class Findings extends Component {
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 {
@@ -506,6 +490,21 @@ class Findings extends Component {
});
};
+ // 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,
@@ -541,7 +540,7 @@ class Findings extends Component {
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'] =
@@ -565,8 +564,8 @@ class Findings extends Component {
content: (
<>
- {this.getFindingsGraph(findings, loading)}
-
+ {/*{this.getFindingsGraph(findings, loading)}*/}
+ {/**/}
{
content: (
<>
- {this.getFindingsGraph(findings, loading)}
-
+ {/*{this.getFindingsGraph(findings, loading)}*/}
+ {/**/}
{
}
/>
) : (
-
+
+
+
)}
diff --git a/public/pages/Overview/components/Widgets/Summary.tsx b/public/pages/Overview/components/Widgets/Summary.tsx
index 910aa555b..226570dbf 100644
--- a/public/pages/Overview/components/Widgets/Summary.tsx
+++ b/public/pages/Overview/components/Widgets/Summary.tsx
@@ -16,19 +16,12 @@ import {
import React, { useCallback, useEffect, useState } from 'react';
import { WidgetContainer } from './WidgetContainer';
import { summaryGroupByOptions } from '../../utils/constants';
-import {
- getChartTimeUnit,
- getDomainRange,
- getOverviewVisualizationSpec,
- getTimeWithMinPrecision,
- TimeUnit,
-} from '../../utils/helpers';
-import { createSelectComponent, renderVisualization } from '../../../../utils/helpers';
+import { TimeUnit } from '../../utils/helpers';
+import { createSelectComponent } from '../../../../utils/helpers';
import { ROUTES } from '../../../../utils/constants';
-import { ChartContainer } from '../../../../components/Charts/ChartContainer';
-import { getLogTypeLabel } from '../../../LogTypes/utils/helpers';
import { OverviewAlertItem, OverviewFindingItem } from '../../../../../types';
import { getUseUpdatedUx } from '../../../../services/utils/constants';
+// import { createBarAndLineChartWrapper } from '../../../../utils/chartUtils';
export interface SummaryProps {
findings: OverviewFindingItem[];
@@ -39,12 +32,7 @@ export interface SummaryProps {
timeUnit: TimeUnit;
}
-export interface SummaryData {
- time: number;
- alert: number;
- finding: number;
- logType?: string;
-}
+export const SUMMARY_VIEW_CHART = 'summary-view';
export const Summary: React.FC = ({
alerts,
@@ -54,7 +42,8 @@ export const Summary: React.FC = ({
loading = false,
}) => {
const [groupBy, setGroupBy] = useState('');
- const [summaryData, setSummaryData] = useState([]);
+ const [alertsVisData, setAlertsVisData] = useState([]);
+ const [findingsVisData, setFindingsVisData] = useState([]);
const [activeAlerts, setActiveAlerts] = useState(undefined);
const [totalFindings, setTotalFindings] = useState(undefined);
@@ -76,51 +65,44 @@ export const Summary: React.FC = ({
[onGroupByChange]
);
- const generateVisualizationSpec = useCallback(
- (summaryData, groupBy) => {
- const chartTimeUnits = getChartTimeUnit(startTime, endTime);
- return getOverviewVisualizationSpec(summaryData, groupBy, {
- timeUnit: chartTimeUnits.timeUnit,
- dateFormat: chartTimeUnits.dateFormat,
- domain: getDomainRange([startTime, endTime], chartTimeUnits.timeUnit.unit),
- });
- },
- [startTime, endTime]
- );
-
useEffect(() => {
- const summaryData: SummaryData[] = [];
+ const alertsVisData: any[] = [];
let activeAlerts = 0;
alerts.forEach((alert) => {
if (!alert.acknowledged) {
activeAlerts++;
}
- summaryData.push({
- time: getTimeWithMinPrecision(alert.time),
+ alertsVisData.push({
+ time: alert.time,
alert: 1,
finding: 0,
- logType: getLogTypeLabel(alert.logType),
+ logType: alert.logType,
});
});
+ const findingsVisData: any[] = [];
findings.forEach((finding) => {
- summaryData.push({
- time: getTimeWithMinPrecision(finding.time),
+ findingsVisData.push({
+ time: finding.time,
alert: 0,
finding: 1,
- logType: getLogTypeLabel(finding.logType),
+ logType: finding.logType,
});
});
setActiveAlerts(activeAlerts);
setTotalFindings(findings.length);
- setSummaryData(summaryData);
+ setAlertsVisData(alertsVisData);
+ setFindingsVisData(findingsVisData);
}, [alerts, findings]);
- useEffect(() => {
- renderVisualization(generateVisualizationSpec(summaryData, groupBy), 'summary-view');
- }, [summaryData, groupBy]);
+ // useEffect(() => {
+ // createBarAndLineChartWrapper(alertsVisData, findingsVisData, groupBy, SUMMARY_VIEW_CHART, {
+ // startTime,
+ // endTime,
+ // });
+ // }, [alertsVisData, findingsVisData, groupBy]);
const createStatComponent = useCallback(
(description: string, urlData: { url: string; color: EuiLinkColor }, stat?: number) => (
@@ -167,39 +149,41 @@ export const Summary: React.FC = ({
)}
)}
-
- {activeAlerts === 0 && totalFindings === 0 ? (
-
- No alerts and findings found
-
- }
- body={
- <>
-
-
- Adjust the time range to see more results or create a
- detector to generate findings.
-
-
-
- Create a detector
-
- >
- }
- />
- ) : (
-
- )}
-
+ {/**/}
+ {/* {activeAlerts === 0 && totalFindings === 0 ? (*/}
+ {/* */}
+ {/* No alerts and findings found
*/}
+ {/* */}
+ {/* }*/}
+ {/* body={*/}
+ {/* <>*/}
+ {/* */}
+ {/* */}
+ {/* Adjust the time range to see more results or create a
*/}
+ {/* detector to generate findings.*/}
+ {/* */}
+ {/*
*/}
+ {/* */}
+ {/* Create a detector*/}
+ {/* */}
+ {/* >*/}
+ {/* }*/}
+ {/* />*/}
+ {/* ) : (*/}
+ {/* */}
+ {/* */}
+ {/*
*/}
+ {/* )}*/}
+ {/**/}
);
diff --git a/public/pages/Overview/components/Widgets/TopRulesWidget.tsx b/public/pages/Overview/components/Widgets/TopRulesWidget.tsx
index b6ef0a512..1b0407831 100644
--- a/public/pages/Overview/components/Widgets/TopRulesWidget.tsx
+++ b/public/pages/Overview/components/Widgets/TopRulesWidget.tsx
@@ -3,12 +3,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { getEuiEmptyPrompt, renderVisualization } from '../../../../utils/helpers';
+import { getEuiEmptyPrompt } from '../../../../utils/helpers';
import React, { useEffect } from 'react';
import { WidgetContainer } from './WidgetContainer';
-import { getTopRulesVisualizationSpec } from '../../utils/helpers';
-import { ChartContainer } from '../../../../components/Charts/ChartContainer';
import { OverviewFindingItem } from '../../../../../types';
+// import { createDoughnutChartWrapper } from '../../../../utils/chartUtils';
export interface TopRulesWidgetProps {
findings: OverviewFindingItem[];
@@ -17,6 +16,8 @@ export interface TopRulesWidgetProps {
type RulesCount = { [ruleName: string]: number };
+export const TOP_RULES_VIEW_CHART = 'top-rules-view';
+
export const TopRulesWidget: React.FC = ({ findings, loading = false }) => {
useEffect(() => {
const rulesCount: RulesCount = {};
@@ -29,7 +30,7 @@ export const TopRulesWidget: React.FC = ({ findings, loadin
ruleName,
count: rulesCount[ruleName],
}));
- renderVisualization(getTopRulesVisualizationSpec(visualizationData), 'top-rules-view');
+ // createDoughnutChartWrapper(visualizationData, TOP_RULES_VIEW_CHART);
}
}, [findings]);
@@ -38,7 +39,9 @@ export const TopRulesWidget: React.FC = ({ findings, loadin
{findings.length === 0 ? (
getEuiEmptyPrompt('No findings with detection rules.')
) : (
-
+
+
+
)}
);
diff --git a/public/pages/Overview/containers/Overview/Overview.tsx b/public/pages/Overview/containers/Overview/Overview.tsx
index cd98ec273..ec1fc07e0 100644
--- a/public/pages/Overview/containers/Overview/Overview.tsx
+++ b/public/pages/Overview/containers/Overview/Overview.tsx
@@ -9,9 +9,6 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
- EuiSuperDatePicker,
- EuiTitle,
- EuiSpacer,
EuiSmallButton,
EuiCard,
EuiPanel,
@@ -316,7 +313,7 @@ export const Overview: React.FC = (props) => {
-
+ {/**/}
{
});
});
- describe('tests getVisualizationSpec function', () => {
- const result = getVisualizationSpec(description, data, [layer]);
- it(' - snapshot test', () => {
- expect(result).toMatchSnapshot('should match visualization spec');
- });
- });
-
- describe('tests getOverviewVisualizationSpec function', () => {
- const result = getOverviewVisualizationSpec([], '', dateOpts);
- it(' - snapshot test', () => {
- expect(result).toMatchSnapshot('should match overview spec');
- });
- });
-
- describe('tests getFindingsVisualizationSpec function', () => {
- const result = getFindingsVisualizationSpec([], '', dateOpts);
- it(' - snapshot test', () => {
- expect(result).toMatchSnapshot('should match findings spec');
- });
- });
-
- describe('tests getAlertsVisualizationSpec function', () => {
- const result = getAlertsVisualizationSpec([], '', dateOpts);
- it(' - snapshot test', () => {
- expect(result).toMatchSnapshot('should match alerts spec');
- });
- });
-
describe('tests getTimeWithMinPrecision function', () => {
const result = getTimeWithMinPrecision('2022/12/01 01:01:01');
it(' - test should be with ms and seconds eq to 0', () => {
diff --git a/public/pages/Overview/utils/helpers.ts b/public/pages/Overview/utils/helpers.ts
index 92caba7ac..7daa8efd8 100644
--- a/public/pages/Overview/utils/helpers.ts
+++ b/public/pages/Overview/utils/helpers.ts
@@ -3,13 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { euiPaletteColorBlind, euiPaletteForStatus } from '@elastic/eui';
-import { TopLevelSpec } from 'vega-lite';
-import { SummaryData } from '../components/Widgets/Summary';
import dateMath from '@elastic/datemath';
import _ from 'lodash';
import { DEFAULT_DATE_RANGE } from '../../../utils/constants';
-import { severityOptions } from '../../Alerts/utils/constants';
import moment from 'moment';
import { euiThemeVars } from '@osd/ui-shared-deps/theme';
@@ -98,30 +94,6 @@ export const getTimeTooltip = (dateOpts: DateOpts) => ({
format: '%Y-%m-%d %H:%M:%S',
});
-export function getVisualizationSpec(description: string, data: any, layers: any[]): TopLevelSpec {
- return {
- config: {
- view: { stroke: 'transparent' },
- legend: {
- labelFontSize: 14,
- titleFontWeight: 600,
- titleLineHeight: 21,
- titleFontSize: 14,
- titlePadding: 10,
- rowPadding: 6,
- labelFont:
- '"Inter UI", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
- },
- },
- $schema: 'https://vega.github.io/schema/vega-lite/v5.json',
- description: description,
- data: {
- values: data,
- },
- layer: layers,
- };
-}
-
/**
* Recalculates vertical domain range to add a bit of space
* so that the topmost items in the chart are not clipped
@@ -141,279 +113,6 @@ export const getYDomainRange = (data: any[], timeUnit: string): number[] => {
return [0, domainMax.length + 0.1];
};
-export function getOverviewVisualizationSpec(
- visualizationData: SummaryData[],
- groupBy: string,
- dateOpts: DateOpts = {
- timeUnit: defaultTimeUnit,
- dateFormat: defaultDateFormat,
- domain: defaultScaleDomain,
- }
-): TopLevelSpec {
- const findingsEncoding: { [x: string]: any } = {
- x: getXAxis(dateOpts),
- y: getYAxis('finding', 'Count', true, {
- scale: {
- domain: getYDomainRange(visualizationData, dateOpts.timeUnit.unit),
- },
- }),
- tooltip: [getYAxis('finding', 'Findings'), getTimeTooltip(dateOpts)],
- color: {
- field: 'fieldType',
- title: '',
- legend: {
- values: ['Active alerts', 'Findings'],
- },
- scale: {
- domain: ['Active alerts', 'Findings'],
- range: [alertsDefaultColor, euiPaletteColorBlind()[1]],
- },
- },
- };
-
- let barLayer = {
- mark: {
- type: 'bar',
- clip: true,
- },
- transform: [{ calculate: "datum.alert == 1 ? 'Active alerts' : 'Findings'", as: 'fieldType' }],
- encoding: findingsEncoding,
- };
-
- if (groupBy === 'logType') {
- findingsEncoding['color'] = {
- field: 'logType',
- type: 'nominal',
- title: 'Log type',
- scale: {
- range: euiPaletteColorBlind(),
- },
- };
-
- findingsEncoding['tooltip'].push({
- field: groupBy,
- title: groupBy === 'logType' ? 'Log type' : 'Rule severity',
- });
-
- barLayer = addInteractiveLegends(barLayer);
- }
-
- return getVisualizationSpec(
- 'Plot showing average data with raw values in the background.',
- visualizationData,
- [
- barLayer,
- {
- mark: {
- type: 'line',
- clip: true,
- interpolate: 'monotone',
- color: alertsDefaultColor,
- point: {
- filled: false,
- fill: 'white',
- color: alertsDefaultColor,
- size: 50,
- },
- },
- encoding: {
- x: getXAxis(dateOpts, {
- band: 0.5,
- }),
- y: getYAxis('alert', 'Count'),
- tooltip: [getYAxis('alert', 'Alerts'), getTimeTooltip(dateOpts)],
- },
- },
- ]
- );
-}
-
-export function getFindingsVisualizationSpec(
- visualizationData: any[],
- groupBy: string,
- dateOpts: DateOpts = {
- timeUnit: defaultTimeUnit,
- dateFormat: defaultDateFormat,
- domain: defaultScaleDomain,
- }
-) {
- const severities = ['info', 'low', 'medium', 'high', 'critical'];
- const isGroupedByLogType = groupBy === 'logType';
- const logTitle = 'Log type';
- const severityTitle = 'Rule severity';
- const title = isGroupedByLogType ? logTitle : severityTitle;
- return getVisualizationSpec('Findings data overview', visualizationData, [
- addInteractiveLegends({
- mark: {
- type: 'bar',
- clip: true,
- },
- encoding: {
- tooltip: [
- getYAxis('finding', 'Findings'),
- getTimeTooltip(dateOpts),
- {
- field: groupBy,
- title: title,
- },
- ],
- x: getXAxis(dateOpts),
- y: getYAxis('finding', 'Count'),
- color: {
- field: groupBy,
- title: title,
- scale: {
- domain: isGroupedByLogType ? undefined : severities,
- range: groupBy === 'logType' ? euiPaletteColorBlind() : euiPaletteForStatus(5),
- },
- },
- },
- }),
- ]);
-}
-
-export function getThreatIntelFindingsVisualizationSpec(
- visualizationData: any[],
- groupBy: string,
- dateOpts: DateOpts = {
- timeUnit: defaultTimeUnit,
- dateFormat: defaultDateFormat,
- domain: defaultScaleDomain,
- }
-) {
- const indicatorTypeTitle = 'Indicator type';
-
- return getVisualizationSpec('Findings data overview', visualizationData, [
- addInteractiveLegends({
- mark: {
- type: 'bar',
- clip: true,
- },
- encoding: {
- tooltip: [
- getYAxis('finding', 'Findings'),
- getTimeTooltip(dateOpts),
- {
- field: groupBy,
- title: indicatorTypeTitle,
- },
- ],
- x: getXAxis(dateOpts),
- y: getYAxis('finding', 'Count'),
- color: {
- field: groupBy,
- title: indicatorTypeTitle,
- scale: {
- range: euiPaletteColorBlind(),
- },
- },
- },
- }),
- ]);
-}
-
-export function getAlertsVisualizationSpec(
- visualizationData: any[],
- groupBy: string,
- dateOpts: DateOpts = {
- timeUnit: defaultTimeUnit,
- dateFormat: defaultDateFormat,
- domain: defaultScaleDomain,
- }
-) {
- const isGroupedByStatus = groupBy === 'status';
- let severities = severityOptions.map((severity) => severity.text);
- severities.reverse().pop();
-
- let states = ['ACTIVE', 'ACKNOWLEDGED', 'COMPLETED', 'ERROR'];
- const statusColors = {
- ACTIVE: '#E7664C',
- ACKNOWLEDGED: '#6092C0',
- COMPLETED: '#54B399',
- ERROR: '#B9A888',
- };
-
- const statusTitle = 'Alert status';
- const severityTitle = 'Alert severity';
- const title = isGroupedByStatus ? statusTitle : severityTitle;
- return getVisualizationSpec('Alerts data overview', visualizationData, [
- addInteractiveLegends({
- mark: {
- type: 'bar',
- clip: true,
- },
- encoding: {
- tooltip: [
- getYAxis('alert', 'Alerts'),
- getTimeTooltip(dateOpts),
- {
- field: groupBy,
- title: title,
- },
- ],
- x: getXAxis(dateOpts),
- y: getYAxis('alert', 'Count'),
- color: {
- field: groupBy,
- title: title,
- scale: {
- domain: isGroupedByStatus ? states : severities,
- range: isGroupedByStatus ? Object.values(statusColors) : euiPaletteForStatus(5),
- },
- },
- },
- }),
- ]);
-}
-
-export function getTopRulesVisualizationSpec(visualizationData: any[]) {
- return getVisualizationSpec('Most frequent detection rules', visualizationData, [
- {
- mark: { type: 'arc', innerRadius: 90 },
- transform: [
- {
- joinaggregate: [
- {
- op: 'sum',
- field: 'count',
- as: 'total',
- },
- ],
- },
- {
- calculate: 'datum.count/datum.total',
- as: 'percentage',
- },
- ],
- encoding: {
- tooltip: [
- {
- field: 'percentage',
- title: 'Percentage',
- type: 'quantitative',
- format: '2.0%',
- },
- {
- field: 'ruleName',
- type: 'nominal',
- title: 'Rule',
- },
- getYAxis('count', 'Count'),
- ],
- theta: getYAxis('count', ''),
- color: {
- field: 'ruleName',
- type: 'nominal',
- title: 'Rule name',
- scale: {
- range: euiPaletteColorBlind(),
- },
- },
- },
- },
- ]);
-}
-
export function getTimeWithMinPrecision(time: number | string) {
const date = new Date(time);
date.setSeconds(0);
diff --git a/public/utils/chartUtils.tsx b/public/utils/chartUtils.tsx
new file mode 100644
index 000000000..5cd213d99
--- /dev/null
+++ b/public/utils/chartUtils.tsx
@@ -0,0 +1,610 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// import { euiPaletteColorBlind, euiPaletteForStatus } from '@elastic/eui';
+// import datemath from '@elastic/datemath';
+// import moment from 'moment';
+import { DEFAULT_DATE_RANGE, FindingTabId } from './constants';
+import { alertsGroupByOptions } from '../pages/Alerts/containers/Alerts/Alerts';
+import { groupByOptionsByTabId } from '../pages/Findings/containers/Findings/Findings';
+import { summaryGroupByOptions } from '../pages/Overview/utils/constants';
+// import { getEuiEmptyPrompt } from "./helpers";
+// import {
+// BarController,
+// BarElement,
+// CategoryScale,
+// Chart,
+// Legend,
+// LinearScale,
+// TimeScale,
+// Title,
+// Tooltip,
+// LineElement,
+// PointElement,
+// LineController,
+// ArcElement,
+// DoughnutController,
+// } from 'chart.js';
+// import 'chartjs-adapter-moment';
+
+// /**
+// * This function is intended to be used to prevent the UI page failing to load when
+// * there is a webpack/build issues that makes chart.js unavailable.
+// *
+// * An empty UI panel will appear in place of the chart.
+// *
+// * @param callback
+// * @param container
+// */
+// const initializeChart = (container: string) => {
+// try {
+// // Try to access Chart.js - this will throw if exports is not defined
+// require('chart.js');
+// require('chartjs-adapter-moment');
+// } catch (e) {
+// console.error(`Chart initialization error for ${container}:`, e);
+// const chartContainer = document.getElementById(container)?.parentElement;
+// if (chartContainer) {
+// const errorMessage = getEuiEmptyPrompt('Unable to load chart visualization.');
+// chartContainer.innerHTML = `
+//
+// ${errorMessage}
+//
+// `;
+// }
+// }
+// };
+//
+// export const createBarChartWrapper = (
+// data: any[],
+// groupBy: string,
+// container: string = 'myBarChart',
+// dateTimeFilter = {
+// startTime: DEFAULT_DATE_RANGE.start,
+// endTime: DEFAULT_DATE_RANGE.end,
+// }
+// ) => {
+// initializeChart(container);
+// try {
+// createBarChart(data, groupBy, container, dateTimeFilter);
+// } catch (e) {
+// console.error(`Error while compiling bar chart ${container}`, e);
+// const chartContainer = document.getElementById(container)?.parentElement;
+// if (chartContainer) {
+// chartContainer.innerHTML = `
+//
+// Error while compiling bar chart ${container}
+//
+// `;
+// }
+// }
+// };
+//
+// const createBarChart = (
+// data: any[],
+// groupBy: string,
+// container: string = 'myBarChart',
+// dateTimeFilter = {
+// startTime: DEFAULT_DATE_RANGE.start,
+// endTime: DEFAULT_DATE_RANGE.end,
+// }
+// ) => {
+// // Register the required components
+// Chart.register(
+// CategoryScale,
+// LinearScale,
+// BarController,
+// BarElement,
+// Title,
+// Tooltip,
+// Legend,
+// TimeScale
+// );
+//
+// const groupByOption = getGroupByText(groupBy);
+//
+// const defaultColorPalette = euiPaletteColorBlind();
+// const severityColorPalette = euiPaletteForStatus(5);
+//
+// // Calculate the time difference in milliseconds
+// const start = datemath.parse(dateTimeFilter.startTime)!;
+// const end = datemath.parse(dateTimeFilter.endTime)!;
+// const diffInHours = end?.diff(start, 'hour') || 0;
+//
+// // Determine the appropriate time unit and format based on the range
+// let timeUnit: 'hour' | 'day' | 'week' | 'month';
+// let displayFormat: string;
+// let tickLimit: number;
+// let stepSize: number;
+//
+// if (diffInHours <= 24) {
+// // Last 24 hours - show hourly ticks
+// timeUnit = 'hour';
+// displayFormat = 'HH:mm';
+// tickLimit = 24;
+// stepSize = 1; // Show every hour
+// } else if (diffInHours <= 72) {
+// // Last 3 days - show every 3 hours
+// timeUnit = 'hour';
+// displayFormat = 'MMM D HH:mm';
+// tickLimit = 24;
+// stepSize = 3; // Show every 3 hours
+// } else if (diffInHours <= 168) {
+// // Last week - show daily ticks
+// timeUnit = 'day';
+// displayFormat = 'MMM D';
+// tickLimit = 7;
+// stepSize = 6; // Show every 6 hours
+// } else {
+// // More than a week - show weekly ticks
+// timeUnit = 'week';
+// displayFormat = 'MMM D';
+// tickLimit = 10;
+// stepSize = 12; // Show every 12 hours
+// }
+// // Destroy existing chart if it exists
+// const existingChart = Chart.getChart(container);
+// if (existingChart) {
+// existingChart.destroy();
+// }
+//
+// // Group data by the selected field
+// const uniqueGroups = [...new Set(data.map((item) => (item as any)[groupBy]))];
+//
+// // Group data by time periods
+// const timeLabels = [...new Set(data.map((item) => item.time))]
+// .sort((a, b) => a - b)
+// .map((time) => new Date(time).toLocaleString());
+//
+// // Create datasets for each group
+// const datasets = uniqueGroups.map((group, idx) => {
+// const color = groupBy.includes('severity')
+// ? severityColorPalette[idx % severityColorPalette.length]
+// : defaultColorPalette[idx % defaultColorPalette.length];
+//
+// const counts: Record = {};
+// return {
+// label: group,
+// data: data
+// .filter((item: any) => item[groupBy] === group)
+// .map((item) => {
+// counts[item.time] = (counts[item.time] || 0) + 1;
+// return {
+// x: moment(item.time).toDate(), // Convert to JavaScript Date object
+// y: counts[item.time],
+// };
+// }),
+// backgroundColor: color,
+// borderColor: color,
+// borderWidth: 1,
+// };
+// });
+//
+// const ctx = document.getElementById(container) as HTMLCanvasElement;
+// if (!ctx) {
+// return;
+// }
+//
+// new Chart(ctx, {
+// type: 'bar',
+// data: {
+// labels: timeLabels,
+// datasets: datasets,
+// },
+// options: {
+// responsive: true,
+// scales: {
+// x: {
+// type: 'time',
+// time: {
+// unit: timeUnit, // or 'hour', 'day', etc.
+// displayFormats: {
+// minute: 'HH:mm',
+// hour: 'HH:mm',
+// day: 'MMM d',
+// week: 'MMM d',
+// month: 'MMM yyyy',
+// quarter: 'MMM yyyy',
+// year: 'yyyy',
+// },
+// },
+// bounds: 'ticks',
+// offset: true,
+// ticks: {
+// source: 'auto',
+// autoSkip: true,
+// maxRotation: 45,
+// maxTicksLimit: tickLimit, // Adjust this to show more/fewer labels
+// stepSize: stepSize,
+// major: {
+// enabled: true,
+// },
+// callback: function (value) {
+// // Format the tick label
+// return moment(value).format(displayFormat);
+// },
+// },
+// min: start.valueOf(), // Add start time
+// max: end.valueOf(), // Add end time
+// stacked: true,
+// title: {
+// display: true,
+// text: 'Time',
+// },
+// },
+// y: {
+// stacked: true,
+// beginAtZero: true,
+// title: {
+// display: true,
+// text: 'Count',
+// },
+// ticks: {
+// stepSize: 1,
+// precision: 0, // This prevents decimal places
+// },
+// },
+// },
+// plugins: {
+// tooltip: {
+// callbacks: {
+// label: function (context: any) {
+// const label = context.dataset.label || '';
+// const value = context.parsed.y;
+// const timeLabel = context.label;
+// return `${groupByOption.text}: ${label}
+// Count: ${value}
+// Time: ${timeLabel}`;
+// },
+// },
+// },
+// legend: {
+// position: 'top',
+// },
+// title: {
+// display: true,
+// text: `Grouped by ${groupByOption.text}`,
+// },
+// },
+// },
+// });
+// };
+//
+// export const createBarAndLineChartWrapper = (
+// alertData: any[],
+// findingData: any[],
+// groupBy: string,
+// container: string = 'myBarAndLineChart',
+// dateTimeFilter = {
+// startTime: DEFAULT_DATE_RANGE.start,
+// endTime: DEFAULT_DATE_RANGE.end,
+// }
+// ) => {
+// initializeChart(container);
+// try {
+// createBarAndLineChart(alertData, findingData, groupBy, container, dateTimeFilter);
+// } catch (e) {
+// console.error(`Error while compiling bar/line chart ${container}`, e);
+// const chartContainer = document.getElementById(container)?.parentElement;
+// if (chartContainer) {
+// chartContainer.innerHTML = `
+//
+// Error while compiling bar/line chart ${container}
+//
+// `;
+// }
+// }
+// };
+//
+// const createBarAndLineChart = (
+// alertData: any[],
+// findingData: any[],
+// groupBy: string,
+// container: string = 'myChart',
+// dateTimeFilter = {
+// startTime: DEFAULT_DATE_RANGE.start,
+// endTime: DEFAULT_DATE_RANGE.end,
+// }
+// ) => {
+// // Register the required components
+// Chart.register(
+// CategoryScale,
+// LinearScale,
+// BarController,
+// BarElement,
+// Title,
+// Tooltip,
+// Legend,
+// TimeScale,
+// LineElement,
+// PointElement,
+// LineController
+// );
+//
+// const groupByOption = getGroupByText(groupBy);
+//
+// const defaultColorPalette = euiPaletteColorBlind();
+// const severityColorPalette = euiPaletteForStatus(5);
+//
+// // Calculate the time difference in milliseconds
+// const start = datemath.parse(dateTimeFilter.startTime)!;
+// const end = datemath.parse(dateTimeFilter.endTime)!;
+// const diffInHours = end?.diff(start, 'hour') || 0;
+//
+// // Determine the appropriate time unit and format based on the range
+// let timeUnit: 'hour' | 'day' | 'week' | 'month';
+// let displayFormat: string;
+// let tickLimit: number;
+// let stepSize: number;
+//
+// if (diffInHours <= 24) {
+// // Last 24 hours - show hourly ticks
+// timeUnit = 'hour';
+// displayFormat = 'HH:mm';
+// tickLimit = 24;
+// stepSize = 1; // Show every hour
+// } else if (diffInHours <= 72) {
+// // Last 3 days - show every 3 hours
+// timeUnit = 'hour';
+// displayFormat = 'MMM D HH:mm';
+// tickLimit = 24;
+// stepSize = 3; // Show every 3 hours
+// } else if (diffInHours <= 168) {
+// // Last week - show daily ticks
+// timeUnit = 'day';
+// displayFormat = 'MMM D';
+// tickLimit = 7;
+// stepSize = 6; // Show every 6 hours
+// } else {
+// // More than a week - show weekly ticks
+// timeUnit = 'week';
+// displayFormat = 'MMM D';
+// tickLimit = 10;
+// stepSize = 12; // Show every 12 hours
+// }
+// // Destroy existing chart if it exists
+// const existingChart = Chart.getChart(container);
+// if (existingChart) {
+// existingChart.destroy();
+// }
+//
+// // Group data by the selected field
+// const uniqueGroups =
+// groupBy === 'logType'
+// ? [...new Set(findingData.map((item) => (item as any)[groupBy]))]
+// : ['All findings'];
+//
+// // Group data by time periods
+// const timeLabels = [...new Set(findingData.map((item) => item.time))]
+// .sort((a, b) => a - b)
+// .map((time) => new Date(time).toLocaleString());
+//
+// // Create datasets for each group
+// const datasets = uniqueGroups.map((group, idx) => {
+// const color = groupBy.includes('severity')
+// ? severityColorPalette[idx % severityColorPalette.length]
+// : defaultColorPalette[idx % defaultColorPalette.length];
+//
+// const counts: Record = {};
+// return {
+// type: 'bar',
+// order: 2,
+// label: group,
+// data: findingData
+// // Overview page only supports grouping by logType, otherwise all findings should be mapped
+// .filter((item: any) => groupBy !== 'logType' || item[groupBy] === group)
+// .map((item) => {
+// counts[item.time] = (counts[item.time] || 0) + 1;
+// return {
+// x: moment(item.time).toDate(), // Convert to JavaScript Date object
+// y: counts[item.time],
+// };
+// }),
+// backgroundColor: color,
+// borderColor: color,
+// borderWidth: 1,
+// };
+// });
+//
+// const alertCounts: Record = {};
+// alertData.forEach((item) => {
+// alertCounts[item.time] = (alertCounts[item.time] || 0) + 1;
+// });
+// const transData = Object.entries(alertCounts).map(([time, count]) => {
+// return {
+// x: moment(time).toDate(), // Convert to JavaScript Date object
+// y: count,
+// };
+// });
+// console.info(`hurneyt transData =`, transData);
+// const alertsDataset = {
+// type: 'line',
+// order: 1,
+// label: 'Alerts',
+// data: transData,
+// backgroundColor: 'warning',
+// borderColor: 'warning',
+// borderWidth: 1,
+// };
+//
+// const ctx = document.getElementById(container) as HTMLCanvasElement;
+// if (!ctx) {
+// return;
+// }
+//
+// new Chart(ctx, {
+// type: 'bar',
+// data: {
+// labels: timeLabels,
+// datasets: [...datasets, alertsDataset],
+// },
+// options: {
+// responsive: true,
+// scales: {
+// x: {
+// type: 'time',
+// time: {
+// unit: timeUnit, // or 'hour', 'day', etc.
+// displayFormats: {
+// minute: 'HH:mm',
+// hour: 'HH:mm',
+// day: 'MMM d',
+// week: 'MMM d',
+// month: 'MMM yyyy',
+// quarter: 'MMM yyyy',
+// year: 'yyyy',
+// },
+// },
+// bounds: 'ticks',
+// offset: true,
+// ticks: {
+// source: 'auto',
+// autoSkip: true,
+// maxRotation: 45,
+// maxTicksLimit: tickLimit, // Adjust this to show more/fewer labels
+// stepSize: stepSize,
+// major: {
+// enabled: true,
+// },
+// callback: function (value) {
+// // Format the tick label
+// return moment(value).format(displayFormat);
+// },
+// },
+// min: start.valueOf(), // Add start time
+// max: end.valueOf(), // Add end time
+// stacked: true,
+// title: {
+// display: true,
+// text: 'Time',
+// },
+// },
+// y: {
+// stacked: true,
+// beginAtZero: true,
+// title: {
+// display: true,
+// text: 'Count',
+// },
+// ticks: {
+// stepSize: 1,
+// precision: 0, // This prevents decimal places
+// },
+// },
+// },
+// plugins: {
+// tooltip: {
+// callbacks: {
+// label: function (context: any) {
+// const label = context.dataset.label || '';
+// const value = context.parsed.y;
+// const timeLabel = context.label;
+// return `${groupByOption.text}: ${label}
+// Count: ${value}
+// Time: ${timeLabel}`;
+// },
+// },
+// },
+// legend: {
+// position: 'top',
+// },
+// title: {
+// display: true,
+// text: `Grouped by ${groupByOption.text}`,
+// },
+// },
+// },
+// });
+// };
+//
+// export const createDoughnutChartWrapper = (
+// visData: {
+// ruleName: string;
+// count: number;
+// }[] = [],
+// container: string = 'myDoughnutChart'
+// ) => {
+// initializeChart(container);
+// try {
+// createDoughnutChart(visData, container);
+// } catch (e) {
+// console.error(`Error while compiling doughnut chart ${container}`, e);
+// const chartContainer = document.getElementById(container)?.parentElement;
+// if (chartContainer) {
+// chartContainer.innerHTML = `
+//
+// Error while compiling doughnut chart ${container}
+//
+// `;
+// }
+// }
+// };
+//
+// const createDoughnutChart = (
+// visData: {
+// ruleName: string;
+// count: number;
+// }[] = [],
+// container: string = 'myDoughnutChart'
+// ) => {
+// // Register the required components
+// Chart.register(
+// DoughnutController, // for doughnut charts
+// ArcElement, // for doughnut/pie charts
+// Tooltip,
+// Legend
+// );
+//
+// const ctx = document.getElementById(container) as HTMLCanvasElement;
+// if (!ctx) {
+// return;
+// }
+//
+// const labels: string[] = [];
+// const counts: number[] = [];
+// visData.forEach((item) => {
+// labels.push(item.ruleName);
+// counts.push(item.count);
+// });
+// new Chart(ctx, {
+// type: 'doughnut',
+// data: {
+// labels: labels,
+// datasets: [
+// {
+// data: counts,
+// backgroundColor: euiPaletteColorBlind(labels.length),
+// },
+// ],
+// },
+// options: {
+// responsive: true,
+// plugins: {
+// legend: {
+// position: 'top',
+// },
+// tooltip: {
+// callbacks: {
+// label: function (context) {
+// const label = context.label || '';
+// const value = context.parsed;
+// return `${label}: ${value}`;
+// },
+// },
+// },
+// },
+// },
+// });
+// };
+
+const getGroupByText = (groupByValue: string) => {
+ const allOptions = [
+ ...alertsGroupByOptions,
+ ...groupByOptionsByTabId[FindingTabId.DetectionRules],
+ ...groupByOptionsByTabId[FindingTabId.ThreatIntel],
+ ...summaryGroupByOptions,
+ ];
+ return allOptions.filter((item) => item.value === groupByValue)[0] || { text: '-', value: '-' };
+};
diff --git a/public/utils/helpers.tsx b/public/utils/helpers.tsx
index eed5c5ad7..d4009bb3a 100644
--- a/public/utils/helpers.tsx
+++ b/public/utils/helpers.tsx
@@ -58,10 +58,6 @@ import { LogCategoryOptionView } from '../components/Utility/LogCategoryOption';
import { getLogTypeLabel } from '../pages/LogTypes/utils/helpers';
import { euiThemeVars } from '@osd/ui-shared-deps/theme';
import dateMath from '@elastic/datemath';
-import { parse, View } from 'vega/build-es5/vega.js';
-import { compile } from 'vega-lite';
-import { Handler } from 'vega-tooltip';
-import { expressionInterpreter as vegaExpressionInterpreter } from 'vega-interpreter/build/vega-interpreter';
import {
getBreadCrumbsSetter,
getBrowserServices,
@@ -221,80 +217,6 @@ export function getUpdatedEnabledRuleIds(
return newEnabledIds;
}
-export async function renderVisualization(spec: any, containerId: string) {
- let view;
-
- try {
- setDefaultColors(spec);
- renderVegaSpec(compile({ ...spec, width: 'container', height: 400 }).spec).catch((err: Error) =>
- console.error(err)
- );
- } catch (error) {
- console.error(error);
- }
-
- async function renderVegaSpec(spec: {}) {
- let chartColoredItems: any[] = [];
- const handler = new Handler({
- formatTooltip: (value, sanitize) => {
- let tooltipData = { ...value };
- let values = Object.entries(tooltipData);
- if (!values.length) return '';
- const tooltipItem = chartColoredItems.filter((groupItem: any) =>
- _.isEqual(groupItem.tooltip, tooltipData)
- );
- const color = tooltipItem.length
- ? tooltipItem[0].fill || tooltipItem[0].stroke
- : 'transparent';
-
- const firstItem = values.pop() || ['', ''];
-
- let rowData = '';
- values.forEach((item: any) => {
- rowData += `
-
- | ${sanitize(item[0])} |
- ${sanitize(item[1])} |
-
- `;
- });
-
- return `
-
- `;
- },
- });
- view = new View(parse(spec, undefined, { expr: vegaExpressionInterpreter } as any), {
- renderer: 'canvas', // renderer (canvas or svg)
- container: `#${containerId}`, // parent DOM container
- hover: true, // enable hover processing
- });
- view.tooltip(handler.call);
- return view.runAsync().then((view: any) => {
- const items = view.scenegraph().root.items[0].items || [];
- const groups = items.filter(
- (item: any) => item.name && item.name.match(/^(layer_).*(_marks)$/)
- );
- for (let item of groups) {
- chartColoredItems = chartColoredItems.concat(item.items);
- }
- });
- }
-}
-
export function createSelectComponent(
options: EuiSelectOption[],
value: string,
diff --git a/yarn.lock b/yarn.lock
index c142fab61..0c01e34d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -677,6 +677,11 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
+"@kurkle/color@^0.3.0":
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.4.tgz#4d4ff677e1609214fc71c580125ddddd86abcabf"
+ integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==
+
"@napi-rs/wasm-runtime@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.1.1.tgz#ec090e2f46bee2ed0c8486dd9d97ddca836ae30e"
@@ -871,6 +876,13 @@
dependencies:
"@babel/types" "^7.20.7"
+"@types/chart.js@^2.9.37":
+ version "2.9.41"
+ resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.41.tgz#4148cdc87d4f98fad44b2883271cd0fa57f05e0d"
+ integrity sha512-3dvkDvueckY83UyUXtJMalYoH6faOLkWQoaTlJgB4Djde3oORmNP0Jw85HtzTuXyliUHcdp704s0mZFQKio/KQ==
+ dependencies:
+ moment "^2.10.2"
+
"@types/graceful-fs@^4.1.2":
version "4.1.9"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
@@ -1767,6 +1779,18 @@ char-regex@^1.0.2:
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+chart.js@^4.4.8:
+ version "4.4.8"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.8.tgz#54645b638e9d585099bc16b892947b5e6cd2a552"
+ integrity sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==
+ dependencies:
+ "@kurkle/color" "^0.3.0"
+
+chartjs-adapter-moment@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz#0f04c30d330b207c14bfb57dfaae9ce332f09102"
+ integrity sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==
+
check-more-types@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
@@ -4433,7 +4457,7 @@ mkdirp@^0.5.1, mkdirp@^0.5.3:
dependencies:
minimist "^1.2.6"
-moment@>=2.13.0, moment@^2.29.2:
+moment@>=2.13.0, moment@^2.10.2, moment@^2.29.2:
version "2.30.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==