[Security Solution][Notes] - add telemetry (#187362)

This commit is contained in:
Philippe Oberti 2024-07-03 22:36:11 +02:00 committed by GitHub
parent f03fa06d5e
commit e4a44fd23e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 186 additions and 28 deletions

View file

@ -169,31 +169,36 @@ const RowActionComponent = ({
tabType,
]);
const toggleShowNotes = useCallback(
() =>
openFlyout({
right: {
id: DocumentDetailsRightPanelKey,
params: {
id: eventId,
indexName,
scopeId: tableId,
},
const toggleShowNotes = useCallback(() => {
openFlyout({
right: {
id: DocumentDetailsRightPanelKey,
params: {
id: eventId,
indexName,
scopeId: tableId,
},
left: {
id: DocumentDetailsLeftPanelKey,
path: {
tab: LeftPanelNotesTab,
},
params: {
id: eventId,
indexName,
scopeId: tableId,
},
},
left: {
id: DocumentDetailsLeftPanelKey,
path: {
tab: LeftPanelNotesTab,
},
}),
[eventId, indexName, openFlyout, tableId]
);
params: {
id: eventId,
indexName,
scopeId: tableId,
},
},
});
telemetry.reportOpenNoteInExpandableFlyoutClicked({
location: tableId,
});
telemetry.reportDetailsFlyoutOpened({
location: tableId,
panel: 'left',
});
}, [eventId, indexName, openFlyout, tableId, telemetry]);
const Action = controlColumn.rowCellRender;

View file

@ -84,6 +84,8 @@ export enum TelemetryEventTypes {
ManualRuleRunCancelJob = 'Manual Rule Run Cancel Job',
EventLogFilterByRunType = 'Event Log Filter By Run Type',
EventLogShowSourceEventDateRange = 'Event Log -> Show Source -> Event Date Range',
OpenNoteInExpandableFlyoutClicked = 'Open Note In Expandable Flyout Clicked',
AddNoteFromExpandableFlyoutClicked = 'Add Note From Expandable Flyout Clicked',
}
export enum ML_JOB_TELEMETRY_STATUS {

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { TelemetryEvent } from '../../types';
import { TelemetryEventTypes } from '../../constants';
export const openNoteInExpandableFlyoutClickedEvent: TelemetryEvent = {
eventType: TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked,
schema: {
location: {
type: 'text',
_meta: {
description: 'Table ID or timeline ID',
optional: false,
},
},
},
};
export const addNoteFromExpandableFlyoutClickedEvent: TelemetryEvent = {
eventType: TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked,
schema: {
isRelatedToATimeline: {
type: 'boolean',
_meta: {
description: 'If the note was added related to a saved timeline',
optional: false,
},
},
},
};

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { RootSchema } from '@kbn/core/public';
import type { TelemetryEventTypes } from '../../constants';
export interface OpenNoteInExpandableFlyoutClickedParams {
location: string;
}
export interface AddNoteFromExpandableFlyoutClickedParams {
isRelatedToATimeline: boolean;
}
export type NotesTelemetryEventParams =
| OpenNoteInExpandableFlyoutClickedParams
| AddNoteFromExpandableFlyoutClickedParams;
export type NotesTelemetryEvents =
| {
eventType: TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked;
schema: RootSchema<OpenNoteInExpandableFlyoutClickedParams>;
}
| {
eventType: TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked;
schema: RootSchema<AddNoteFromExpandableFlyoutClickedParams>;
};

View file

@ -44,6 +44,10 @@ import {
manualRuleRunOpenModalEvent,
} from './manual_rule_run';
import { eventLogFilterByRunTypeEvent, eventLogShowSourceEventDateRangeEvent } from './event_log';
import {
addNoteFromExpandableFlyoutClickedEvent,
openNoteInExpandableFlyoutClickedEvent,
} from './notes';
const mlJobUpdateEvent: TelemetryEvent = {
eventType: TelemetryEventTypes.MLJobUpdate,
@ -186,4 +190,6 @@ export const telemetryEvents = [
manualRuleRunOpenModalEvent,
eventLogFilterByRunTypeEvent,
eventLogShowSourceEventDateRangeEvent,
openNoteInExpandableFlyoutClickedEvent,
addNoteFromExpandableFlyoutClickedEvent,
];

View file

@ -40,4 +40,6 @@ export const createTelemetryClientMock = (): jest.Mocked<TelemetryClientStart> =
reportManualRuleRunCancelJob: jest.fn(),
reportManualRuleRunExecute: jest.fn(),
reportManualRuleRunOpenModal: jest.fn(),
reportOpenNoteInExpandableFlyoutClicked: jest.fn(),
reportAddNoteFromExpandableFlyoutClicked: jest.fn(),
});

View file

@ -6,6 +6,10 @@
*/
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server';
import type {
AddNoteFromExpandableFlyoutClickedParams,
OpenNoteInExpandableFlyoutClickedParams,
} from './events/notes/types';
import type {
TelemetryClientStart,
ReportAlertsGroupingChangedParams,
@ -195,4 +199,16 @@ export class TelemetryClient implements TelemetryClientStart {
): void {
this.analytics.reportEvent(TelemetryEventTypes.EventLogShowSourceEventDateRange, params);
}
public reportOpenNoteInExpandableFlyoutClicked = (
params: OpenNoteInExpandableFlyoutClickedParams
) => {
this.analytics.reportEvent(TelemetryEventTypes.OpenNoteInExpandableFlyoutClicked, params);
};
public reportAddNoteFromExpandableFlyoutClicked = (
params: AddNoteFromExpandableFlyoutClickedParams
) => {
this.analytics.reportEvent(TelemetryEventTypes.AddNoteFromExpandableFlyoutClicked, params);
};
}

View file

@ -66,6 +66,12 @@ import type {
ReportEventLogShowSourceEventDateRangeParams,
ReportEventLogTelemetryEventParams,
} from './events/event_log/types';
import type {
AddNoteFromExpandableFlyoutClickedParams,
NotesTelemetryEventParams,
NotesTelemetryEvents,
OpenNoteInExpandableFlyoutClickedParams,
} from './events/notes/types';
export * from './events/ai_assistant/types';
export * from './events/alerts_grouping/types';
@ -129,7 +135,8 @@ export type TelemetryEventParams =
| OnboardingHubStepFinishedParams
| OnboardingHubStepLinkClickedParams
| ReportManualRuleRunTelemetryEventParams
| ReportEventLogTelemetryEventParams;
| ReportEventLogTelemetryEventParams
| NotesTelemetryEventParams;
export interface TelemetryClientStart {
reportAlertsGroupingChanged(params: ReportAlertsGroupingChangedParams): void;
@ -183,6 +190,10 @@ export interface TelemetryClientStart {
reportEventLogShowSourceEventDateRange(
params: ReportEventLogShowSourceEventDateRangeParams
): void;
// new notes
reportOpenNoteInExpandableFlyoutClicked(params: OpenNoteInExpandableFlyoutClickedParams): void;
reportAddNoteFromExpandableFlyoutClicked(params: AddNoteFromExpandableFlyoutClickedParams): void;
}
export type TelemetryEvent =
@ -209,4 +220,5 @@ export type TelemetryEvent =
}
| OnboardingHubTelemetryEvent
| ManualRuleRunTelemetryEvent
| EventLogTelemetryEvent;
| EventLogTelemetryEvent
| NotesTelemetryEvents;

View file

@ -20,6 +20,7 @@ import {
import { css } from '@emotion/react';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../../../../common/lib/kibana';
import { TimelineId } from '../../../../../common/types';
import { timelineSelectors } from '../../../../timelines/store';
import { useIsTimelineFlyoutOpen } from '../../shared/hooks/use_is_timeline_flyout_open';
@ -80,6 +81,7 @@ export interface AddNewNoteProps {
* The checkbox is automatically checked if the flyout is opened from a timeline and that timeline is saved. It is disabled if the flyout is NOT opened from a timeline.
*/
export const AddNote = memo(({ eventId }: AddNewNoteProps) => {
const { telemetry } = useKibana().services;
const dispatch = useDispatch();
const { addError: addErrorToast } = useAppToasts();
const [editorValue, setEditorValue] = useState('');
@ -110,8 +112,11 @@ export const AddNote = memo(({ eventId }: AddNewNoteProps) => {
},
})
);
telemetry.reportAddNoteFromExpandableFlyoutClicked({
isRelatedToATimeline: checked && activeTimeline?.savedObjectId !== null,
});
setEditorValue('');
}, [activeTimeline?.savedObjectId, checked, dispatch, editorValue, eventId]);
}, [activeTimeline?.savedObjectId, checked, dispatch, editorValue, eventId, telemetry]);
// show a toast if the create note call fails
useEffect(() => {

View file

@ -14,6 +14,7 @@ import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { DocumentDetailsRightPanelKey } from '../../../../../flyout/document_details/shared/constants/panel_keys';
import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector';
import { useKibana } from '../../../../../common/lib/kibana';
import type {
ColumnHeaderOptions,
CellValueElementProps,
@ -107,6 +108,7 @@ const StatefulEventComponent: React.FC<Props> = ({
trailingControlColumns,
onToggleShowNotes,
}) => {
const { telemetry } = useKibana().services;
const trGroupRef = useRef<HTMLDivElement | null>(null);
const dispatch = useDispatch();
@ -224,6 +226,10 @@ const StatefulEventComponent: React.FC<Props> = ({
},
},
});
telemetry.reportDetailsFlyoutOpened({
location: timelineId,
panel: 'right',
});
} else {
// opens the panel when clicking on the table row action
dispatch(
@ -241,6 +247,7 @@ const StatefulEventComponent: React.FC<Props> = ({
expandableFlyoutDisabled,
openFlyout,
timelineId,
telemetry,
dispatch,
tabType,
]);

View file

@ -38,6 +38,7 @@ import type {
DroppableStateSnapshot,
} from '@hello-pangea/dnd';
import { DocumentDetailsRightPanelKey } from '../../../../flyout/document_details/shared/constants/panel_keys';
import { createTelemetryServiceMock } from '../../../../common/lib/telemetry/telemetry_service.mock';
jest.mock('../../../../common/hooks/use_app_toasts');
jest.mock('../../../../common/components/guided_onboarding_tour/tour_step');
@ -104,6 +105,8 @@ jest.mock('@kbn/expandable-flyout', () => {
};
});
const mockedTelemetry = createTelemetryServiceMock();
jest.mock('../../../../common/components/link_to', () => {
const originalModule = jest.requireActual('../../../../common/components/link_to');
return {
@ -255,6 +258,7 @@ describe('Body', () => {
savedObjects: {
client: {},
},
telemetry: mockedTelemetry,
timelines: {
getLastUpdated: jest.fn(),
getLoadingPanel: jest.fn(),

View file

@ -17,6 +17,7 @@ import type { EuiDataGridControlColumn } from '@elastic/eui';
import { DataLoadingState } from '@kbn/unified-data-table';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useKibana } from '../../../../../common/lib/kibana';
import {
DocumentDetailsLeftPanelKey,
DocumentDetailsRightPanelKey,
@ -87,6 +88,7 @@ export const EqlTabContentComponent: React.FC<Props> = ({
pinnedEventIds,
eventIdToNoteIds,
}) => {
const { telemetry } = useKibana().services;
const dispatch = useDispatch();
const { query: eqlQuery = '', ...restEqlOption } = eqlOptions;
const { portalNode: eqlEventsCountPortalNode } = useEqlEventsCountPortal();
@ -188,6 +190,13 @@ export const EqlTabContentComponent: React.FC<Props> = ({
},
},
});
telemetry.reportOpenNoteInExpandableFlyoutClicked({
location: timelineId,
});
telemetry.reportDetailsFlyoutOpened({
location: timelineId,
panel: 'left',
});
} else {
if (eventId) {
setNotesEventId(eventId);
@ -200,6 +209,7 @@ export const EqlTabContentComponent: React.FC<Props> = ({
openFlyout,
securitySolutionNotesEnabled,
selectedPatterns,
telemetry,
timelineId,
setNotesEventId,
showNotesFlyout,

View file

@ -20,6 +20,7 @@ import {
DocumentDetailsRightPanelKey,
} from '../../../../../flyout/document_details/shared/constants/panel_keys';
import type { ControlColumnProps } from '../../../../../../common/types';
import { useKibana } from '../../../../../common/lib/kibana';
import { timelineActions, timelineSelectors } from '../../../../store';
import type { Direction } from '../../../../../../common/search_strategy';
import { useTimelineEvents } from '../../../../containers';
@ -94,6 +95,7 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
expandedDetail,
eventIdToNoteIds,
}) => {
const { telemetry } = useKibana().services;
const {
browserFields,
dataViewId,
@ -224,6 +226,13 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
},
},
});
telemetry.reportOpenNoteInExpandableFlyoutClicked({
location: timelineId,
});
telemetry.reportDetailsFlyoutOpened({
location: timelineId,
panel: 'left',
});
} else {
if (eventId) {
setNotesEventId(eventId);
@ -236,6 +245,7 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
openFlyout,
securitySolutionNotesEnabled,
selectedPatterns,
telemetry,
timelineId,
setNotesEventId,
showNotesFlyout,

View file

@ -116,7 +116,7 @@ export const QueryTabContentComponent: React.FC<Props> = ({
selectedPatterns,
} = useSourcererDataView(SourcererScopeName.timeline);
const { uiSettings, timelineDataService } = useKibana().services;
const { uiSettings, telemetry, timelineDataService } = useKibana().services;
const {
query: { filterManager: timelineFilterManager },
} = timelineDataService;
@ -256,6 +256,13 @@ export const QueryTabContentComponent: React.FC<Props> = ({
},
},
});
telemetry.reportOpenNoteInExpandableFlyoutClicked({
location: timelineId,
});
telemetry.reportDetailsFlyoutOpened({
location: timelineId,
panel: 'left',
});
} else {
if (eventId) {
setNotesEventId(eventId);
@ -268,6 +275,7 @@ export const QueryTabContentComponent: React.FC<Props> = ({
openFlyout,
securitySolutionNotesEnabled,
selectedPatterns,
telemetry,
timelineId,
showNotesFlyout,
setNotesEventId,

View file

@ -134,6 +134,7 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
storage,
dataViewFieldEditor,
notifications: { toasts: toastsService },
telemetry,
theme,
data: dataPluginContract,
},
@ -187,6 +188,10 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
},
},
});
telemetry.reportDetailsFlyoutOpened({
location: timelineId,
panel: 'right',
});
} else {
dispatch(
timelineActions.toggleDetailPanel({
@ -199,7 +204,7 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
activeTimeline.toggleExpandedDetail({ ...updatedExpandedDetail });
},
[activeTab, dispatch, refetch, timelineId, isExpandableFlyoutDisabled, openFlyout]
[refetch, isExpandableFlyoutDisabled, openFlyout, timelineId, telemetry, dispatch, activeTab]
);
const onTimelineLegacyFlyoutClose = useCallback(() => {