Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SecuritySolution] Generic reportEvents for EBT Telemetry #197079

Merged
merged 10 commits into from
Nov 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { StartServices } from '../../types';
import { enhanceActionWithTelemetry } from './telemetry';
import { createAction } from '@kbn/ui-actions-plugin/public';
import type { CellActionExecutionContext } from '@kbn/cell-actions';
import { AppEventTypes } from '../../common/lib/telemetry';

const actionId = 'test_action_id';
const displayName = 'test-actions';
Expand All @@ -29,13 +30,13 @@ const context = {

describe('enhanceActionWithTelemetry', () => {
it('calls telemetry report when the action is executed', () => {
const telemetry = { reportCellActionClicked: jest.fn() };
const telemetry = { reportEvent: jest.fn() };
const services = { telemetry } as unknown as StartServices;

const enhancedAction = enhanceActionWithTelemetry(action, services);
enhancedAction.execute(context);

expect(telemetry.reportCellActionClicked).toHaveBeenCalledWith({
expect(telemetry.reportEvent).toHaveBeenCalledWith(AppEventTypes.CellActionClicked, {
displayName,
actionId,
fieldName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { CellAction, CellActionExecutionContext } from '@kbn/cell-actions';
import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public';
import type { StartServices } from '../../types';
import type { SecurityCellActionExecutionContext } from './types';
import { AppEventTypes } from '../../common/lib/telemetry';

export const enhanceActionWithTelemetry = (
action: CellAction<CellActionExecutionContext>,
Expand All @@ -19,7 +20,7 @@ export const enhanceActionWithTelemetry = (
const enhancedExecute = (
context: ActionExecutionContext<SecurityCellActionExecutionContext>
): Promise<void> => {
telemetry.reportCellActionClicked({
telemetry.reportEvent(AppEventTypes.CellActionClicked, {
actionId: rest.id,
displayName: rest.getDisplayName(context),
fieldName: context.data.map(({ field }) => field.name).join(', '),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { renderHook } from '@testing-library/react-hooks';
import { useAssistantTelemetry } from '.';
import { BASE_SECURITY_CONVERSATIONS } from '../content/conversations';
import { createTelemetryServiceMock } from '../../common/lib/telemetry/telemetry_service.mock';
import { AssistantEventTypes } from '../../common/lib/telemetry';

const customId = `My Convo`;
const mockedConversations = {
Expand All @@ -20,15 +21,9 @@ const mockedConversations = {
messages: [],
},
};
const reportAssistantInvoked = jest.fn();
const reportAssistantMessageSent = jest.fn();
const reportAssistantQuickPrompt = jest.fn();

const mockedTelemetry = {
...createTelemetryServiceMock(),
reportAssistantInvoked,
reportAssistantMessageSent,
reportAssistantQuickPrompt,
reportAssistantSettingToggled: () => {},
};

jest.mock('../../common/lib/kibana', () => {
Expand All @@ -55,9 +50,9 @@ jest.mock('@kbn/elastic-assistant', () => ({
}));

const trackingFns = [
'reportAssistantInvoked',
'reportAssistantMessageSent',
'reportAssistantQuickPrompt',
{ name: 'reportAssistantInvoked', eventType: AssistantEventTypes.AssistantInvoked },
{ name: 'reportAssistantMessageSent', eventType: AssistantEventTypes.AssistantMessageSent },
{ name: 'reportAssistantQuickPrompt', eventType: AssistantEventTypes.AssistantQuickPrompt },
];

describe('useAssistantTelemetry', () => {
Expand All @@ -67,7 +62,7 @@ describe('useAssistantTelemetry', () => {
it('should return the expected telemetry object with tracking functions', () => {
const { result } = renderHook(() => useAssistantTelemetry());
trackingFns.forEach((fn) => {
expect(result.current).toHaveProperty(fn);
expect(result.current).toHaveProperty(fn.name);
});
});

Expand All @@ -76,11 +71,11 @@ describe('useAssistantTelemetry', () => {
const { result } = renderHook(() => useAssistantTelemetry());
const validId = Object.keys(mockedConversations)[0];
// @ts-ignore
const trackingFn = result.current[fn];
const trackingFn = result.current[fn.name];
await trackingFn({ conversationId: validId, invokedBy: 'shortcut' });
// @ts-ignore
const trackingMockedFn = mockedTelemetry[fn];
expect(trackingMockedFn).toHaveBeenCalledWith({
const trackingMockedFn = mockedTelemetry.reportEvent;
expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, {
conversationId: validId,
invokedBy: 'shortcut',
});
Expand All @@ -89,11 +84,11 @@ describe('useAssistantTelemetry', () => {
it('Should call tracking with "Custom" id when tracking is called with an isDefault=false conversation id', async () => {
const { result } = renderHook(() => useAssistantTelemetry());
// @ts-ignore
const trackingFn = result.current[fn];
const trackingFn = result.current[fn.name];
await trackingFn({ conversationId: customId, invokedBy: 'shortcut' });
// @ts-ignore
const trackingMockedFn = mockedTelemetry[fn];
expect(trackingMockedFn).toHaveBeenCalledWith({
const trackingMockedFn = mockedTelemetry.reportEvent;
expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, {
conversationId: 'Custom',
invokedBy: 'shortcut',
});
Expand All @@ -102,11 +97,11 @@ describe('useAssistantTelemetry', () => {
it('Should call tracking with "Custom" id when tracking is called with an unknown conversation id', async () => {
const { result } = renderHook(() => useAssistantTelemetry());
// @ts-ignore
const trackingFn = result.current[fn];
const trackingFn = result.current[fn.name];
await trackingFn({ conversationId: '123', invokedBy: 'shortcut' });
// @ts-ignore
const trackingMockedFn = mockedTelemetry[fn];
expect(trackingMockedFn).toHaveBeenCalledWith({
const trackingMockedFn = mockedTelemetry.reportEvent;
expect(trackingMockedFn).toHaveBeenCalledWith(fn.eventType, {
conversationId: 'Custom',
invokedBy: 'shortcut',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { type AssistantTelemetry } from '@kbn/elastic-assistant';
import { useCallback } from 'react';
import { useKibana } from '../../common/lib/kibana';
import { useBaseConversations } from '../use_conversation_store';

import type {
ReportAssistantInvokedParams,
ReportAssistantMessageSentParams,
ReportAssistantQuickPromptParams,
ReportAssistantSettingToggledParams,
} from '../../common/lib/telemetry';
import { AssistantEventTypes } from '../../common/lib/telemetry';
export const useAssistantTelemetry = (): AssistantTelemetry => {
const {
services: { telemetry },
Expand All @@ -27,27 +33,30 @@ export const useAssistantTelemetry = (): AssistantTelemetry => {

const reportTelemetry = useCallback(
async ({
fn,
eventType,
params: { conversationId, ...rest },
}: // eslint-disable-next-line @typescript-eslint/no-explicit-any
any): Promise<{
fn: keyof AssistantTelemetry;
params: AssistantTelemetry[keyof AssistantTelemetry];
}> =>
fn({
}: {
eventType: AssistantEventTypes;
params:
| ReportAssistantInvokedParams
| ReportAssistantMessageSentParams
| ReportAssistantQuickPromptParams;
}) =>
telemetry.reportEvent(eventType, {
...rest,
conversationId: await getAnonymizedConversationTitle(conversationId),
}),
[getAnonymizedConversationTitle]
[getAnonymizedConversationTitle, telemetry]
);

return {
reportAssistantInvoked: (params) =>
reportTelemetry({ fn: telemetry.reportAssistantInvoked, params }),
reportAssistantMessageSent: (params) =>
reportTelemetry({ fn: telemetry.reportAssistantMessageSent, params }),
reportAssistantQuickPrompt: (params) =>
reportTelemetry({ fn: telemetry.reportAssistantQuickPrompt, params }),
reportAssistantSettingToggled: (params) => telemetry.reportAssistantSettingToggled(params),
reportAssistantInvoked: (params: ReportAssistantInvokedParams) =>
reportTelemetry({ eventType: AssistantEventTypes.AssistantInvoked, params }),
reportAssistantMessageSent: (params: ReportAssistantMessageSentParams) =>
reportTelemetry({ eventType: AssistantEventTypes.AssistantMessageSent, params }),
reportAssistantQuickPrompt: (params: ReportAssistantQuickPromptParams) =>
reportTelemetry({ eventType: AssistantEventTypes.AssistantQuickPrompt, params }),
reportAssistantSettingToggled: (params: ReportAssistantSettingToggledParams) =>
telemetry.reportEvent(AssistantEventTypes.AssistantSettingToggled, params),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import * as timelineMarkdownPlugin from '../../common/components/markdown_editor
import { useFetchAlertData } from './use_fetch_alert_data';
import { useUpsellingMessage } from '../../common/hooks/use_upselling';
import { useFetchNotes } from '../../notes/hooks/use_fetch_notes';
import { DocumentEventTypes } from '../../common/lib/telemetry';

const CaseContainerComponent: React.FC = () => {
const { cases, telemetry } = useKibana().services;
Expand All @@ -47,7 +48,7 @@ const CaseContainerComponent: React.FC = () => {
},
},
});
telemetry.reportDetailsFlyoutOpened({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, {
location: TimelineId.casePage,
panel: 'right',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/t
import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features';
import { useTourContext } from '../../guided_onboarding_tour';
import { AlertsCasesTourSteps, SecurityStepId } from '../../guided_onboarding_tour/tour_config';
import { NotesEventTypes, DocumentEventTypes } from '../../../lib/telemetry';
import { getMappedNonEcsValue } from '../../../utils/get_mapped_non_ecs_value';

export type RowActionProps = EuiDataGridCellValueElementProps & {
Expand Down Expand Up @@ -109,7 +110,7 @@ const RowActionComponent = ({
},
},
});
telemetry.reportDetailsFlyoutOpened({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, {
location: tableId,
panel: 'right',
});
Expand Down Expand Up @@ -137,10 +138,10 @@ const RowActionComponent = ({
},
},
});
telemetry.reportOpenNoteInExpandableFlyoutClicked({
telemetry.reportEvent(NotesEventTypes.OpenNoteInExpandableFlyoutClicked, {
location: tableId,
});
telemetry.reportDetailsFlyoutOpened({
telemetry.reportEvent(DocumentEventTypes.DetailsFlyoutOpened, {
location: tableId,
panel: 'left',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
import type { HostsTableType } from '../../../explore/hosts/store/model';
import type { UsersTableType } from '../../../explore/users/store/model';
import { useGetSecuritySolutionLinkProps, withSecuritySolutionLink } from './link_props';
import { EntityEventTypes } from '../../lib/telemetry';

export { useSecuritySolutionLinkProps, type GetSecuritySolutionLinkProps } from './link_props';
export { LinkButton, LinkAnchor } from './helpers';
Expand Down Expand Up @@ -94,7 +95,7 @@ const UserDetailsLinkComponent: React.FC<{

const onClick = useCallback(
(e: SyntheticEvent) => {
telemetry.reportEntityDetailsClicked({ entity: 'user' });
telemetry.reportEvent(EntityEventTypes.EntityDetailsClicked, { entity: 'user' });
const callback = onClickParam ?? goToUsersDetails;
callback(e);
},
Expand Down Expand Up @@ -171,7 +172,7 @@ const HostDetailsLinkComponent: React.FC<HostDetailsLinkProps> = ({

const onClick = useCallback(
(e: SyntheticEvent) => {
telemetry.reportEntityDetailsClicked({ entity: 'host' });
telemetry.reportEvent(EntityEventTypes.EntityDetailsClicked, { entity: 'host' });

const callback = onClickParam ?? goToHostDetails;
callback(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TestProviders } from '../../../mock';

import type { SecurityJob } from '../types';
import { createTelemetryServiceMock } from '../../../lib/telemetry/telemetry_service.mock';
import { ML_JOB_TELEMETRY_STATUS } from '../../../lib/telemetry';
import { ML_JOB_TELEMETRY_STATUS, EntityEventTypes } from '../../../lib/telemetry';

const wrapper = ({ children }: { children: React.ReactNode }) => (
<TestProviders>{children}</TestProviders>
Expand Down Expand Up @@ -188,14 +188,14 @@ describe('useSecurityJobsHelpers', () => {
await result.current.enableDatafeed(JOB, TIMESTAMP);
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.moduleInstalled,
isElasticJob: true,
jobId,
moduleId,
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.started,
isElasticJob: true,
jobId,
Expand All @@ -211,7 +211,7 @@ describe('useSecurityJobsHelpers', () => {
await result.current.enableDatafeed({ ...JOB, isInstalled: true }, TIMESTAMP);
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.startError,
errorMessage: 'Start job failure - test_error',
isElasticJob: true,
Expand All @@ -228,7 +228,7 @@ describe('useSecurityJobsHelpers', () => {
await result.current.enableDatafeed(JOB, TIMESTAMP);
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.installationError,
errorMessage: 'Create job failure - test_error',
isElasticJob: true,
Expand Down Expand Up @@ -295,7 +295,7 @@ describe('useSecurityJobsHelpers', () => {
await result.current.disableDatafeed({ ...JOB, isInstalled: true });
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.stopped,
isElasticJob: true,
jobId,
Expand All @@ -311,7 +311,7 @@ describe('useSecurityJobsHelpers', () => {
await result.current.disableDatafeed({ ...JOB, isInstalled: true });
});

expect(mockedTelemetry.reportMLJobUpdate).toHaveBeenCalledWith({
expect(mockedTelemetry.reportEvent).toHaveBeenCalledWith(EntityEventTypes.MLJobUpdate, {
status: ML_JOB_TELEMETRY_STATUS.stopError,
errorMessage: 'Stop job failure - test_error',
isElasticJob: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
METRIC_TYPE,
ML_JOB_TELEMETRY_STATUS,
TELEMETRY_EVENT,
EntityEventTypes,
track,
} from '../../../lib/telemetry';

Expand Down Expand Up @@ -43,7 +44,7 @@ export const useEnableDataFeed = () => {
jobIdErrorFilter: [job.id],
groups: job.groups,
});
telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
moduleId: job.moduleId,
Expand All @@ -52,7 +53,7 @@ export const useEnableDataFeed = () => {
} catch (error) {
setIsLoading(false);
addError(error, { title: i18n.CREATE_JOB_FAILURE });
telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
moduleId: job.moduleId,
Expand Down Expand Up @@ -82,7 +83,7 @@ export const useEnableDataFeed = () => {
throw new Error(response[datafeedId].error);
}

telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
status: ML_JOB_TELEMETRY_STATUS.started,
Expand All @@ -92,7 +93,7 @@ export const useEnableDataFeed = () => {
} catch (error) {
track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_ENABLE_FAILURE);
addError(error, { title: i18n.START_JOB_FAILURE });
telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
status: ML_JOB_TELEMETRY_STATUS.startError,
Expand Down Expand Up @@ -124,7 +125,7 @@ export const useEnableDataFeed = () => {
throw new Error(response.error);
}

telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
status: ML_JOB_TELEMETRY_STATUS.stopped,
Expand All @@ -134,7 +135,7 @@ export const useEnableDataFeed = () => {
} catch (error) {
track(METRIC_TYPE.COUNT, TELEMETRY_EVENT.JOB_DISABLE_FAILURE);
addError(error, { title: i18n.STOP_JOB_FAILURE });
telemetry.reportMLJobUpdate({
telemetry.reportEvent(EntityEventTypes.MLJobUpdate, {
jobId: job.id,
isElasticJob: job.isElasticJob,
status: ML_JOB_TELEMETRY_STATUS.stopError,
Expand Down
Loading