[Security Solution][Automatic Migrations] Siem migrations delete from UI (#223983)

## Summary

This pr adds a button with confirm modal to enable deleting finished |
stopped | aborted migrations from the UI cards, the api functionality
already exists. Also changes the rename button to be within a context
menu, and restructures the components around the panels and migration
titles.


![delete_flow](https://github.com/user-attachments/assets/ea1ef02f-e3d6-4b6a-a974-4f225a7b27c1)


### Checklist

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Sergi Massaneda <sergi.massaneda@elastic.co>
This commit is contained in:
Kevin Qualters 2025-06-23 06:57:54 -04:00 committed by GitHub
parent fd7f85149c
commit 1fe758c34f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 487 additions and 18 deletions

View file

@ -17,6 +17,7 @@ export const siemMigrationEventNames = {
[SiemMigrationsEventTypes.SetupConnectorSelected]: 'Connector Selected',
[SiemMigrationsEventTypes.SetupMigrationOpenNew]: 'Open new rules migration',
[SiemMigrationsEventTypes.SetupMigrationCreated]: 'Create new rules migration',
[SiemMigrationsEventTypes.SetupMigrationDeleted]: 'Migration deleted',
[SiemMigrationsEventTypes.SetupResourcesUploaded]: 'Upload rule resources',
[SiemMigrationsEventTypes.SetupMigrationOpenResources]: 'Rules Open Resources',
[SiemMigrationsEventTypes.SetupRulesQueryCopied]: 'Copy rules query',
@ -128,6 +129,11 @@ const eventSchemas: SiemMigrationsTelemetryEventSchemas = {
},
},
},
[SiemMigrationsEventTypes.SetupMigrationDeleted]: {
...migrationIdSchema,
...baseResultActionSchema,
...eventNameSchema,
},
[SiemMigrationsEventTypes.SetupRulesQueryCopied]: {
...eventNameSchema,
migrationId: {

View file

@ -19,6 +19,10 @@ export enum SiemMigrationsEventTypes {
* When Rule Resources are uploaded
*/
SetupMigrationCreated = 'siem_migrations_setup_rules_migration_created',
/**
* When a rules migration is deleted
*/
SetupMigrationDeleted = 'siem_migrations_setup_rules_migration_deleted',
/**
* When new rules are uploaded to create a new migration
*/
@ -92,6 +96,10 @@ export interface ReportSetupMigrationCreatedActionParams extends BaseResultActio
migrationId?: string;
rulesCount: number;
}
export interface ReportSetupMigrationDeletedActionParams extends BaseResultActionParams {
eventName: string;
migrationId: string;
}
export interface ReportSetupMacrosQueryCopiedActionParams {
eventName: string;
migrationId: string;
@ -154,6 +162,7 @@ export interface SiemMigrationsTelemetryEventsMap {
[SiemMigrationsEventTypes.SetupMigrationOpenResources]: ReportSetupMigrationOpenResourcesActionParams;
[SiemMigrationsEventTypes.SetupRulesQueryCopied]: ReportSetupRulesQueryCopiedActionParams;
[SiemMigrationsEventTypes.SetupMigrationCreated]: ReportSetupMigrationCreatedActionParams;
[SiemMigrationsEventTypes.SetupMigrationDeleted]: ReportSetupMigrationDeletedActionParams;
[SiemMigrationsEventTypes.SetupMacrosQueryCopied]: ReportSetupMacrosQueryCopiedActionParams;
[SiemMigrationsEventTypes.SetupLookupNameCopied]: ReportSetupLookupNameCopiedActionParams;
[SiemMigrationsEventTypes.SetupResourcesUploaded]: ReportSetupResourcesUploadedActionParams;

View file

@ -403,3 +403,16 @@ export const updateMigration = async ({
{ version: '1', body: JSON.stringify(body), signal }
);
};
export interface DeleteMigrationParams {
/** `id` of the migration to delete */
migrationId: string;
/** Optional AbortSignal for cancelling request */
signal?: AbortSignal;
}
export const deleteMigration = async ({ migrationId, signal }: DeleteMigrationParams) => {
return KibanaServices.get().http.delete<unknown>(
replaceParams(SIEM_RULE_MIGRATION_PATH, { migration_id: migrationId }),
{ version: '1', signal }
);
};

View file

@ -0,0 +1,287 @@
/*
* 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 React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MigrationPanelTitle } from './migration_panel_title';
import { useUpdateMigration } from '../../logic/use_update_migration';
import { useDeleteMigration } from '../../logic/use_delete_migration';
import { SiemMigrationTaskStatus } from '../../../../../common/siem_migrations/constants';
import { TestProviders } from '../../../../common/mock';
import type { RuleMigrationStats } from '../../types';
import * as i18n from './translations';
jest.mock('../../../../common/lib/kibana/use_kibana');
jest.mock('../../logic/use_update_migration');
const useUpdateMigrationMock = useUpdateMigration as jest.Mock;
const mockUpdateMigration = jest.fn();
jest.mock('../../logic/use_delete_migration');
const useDeleteMigrationMock = useDeleteMigration as jest.Mock;
const mockDeleteMigration = jest.fn();
const mockMigrationStatsReady: RuleMigrationStats = {
id: 'test-migration-id',
name: 'Test Migration',
status: SiemMigrationTaskStatus.READY,
rules: { total: 6, pending: 6, processing: 0, completed: 0, failed: 0 },
created_at: '2025-05-27T12:12:17.563Z',
last_updated_at: '2025-05-27T12:12:17.563Z',
};
const mockMigrationStatsRunning: RuleMigrationStats = {
...mockMigrationStatsReady,
status: SiemMigrationTaskStatus.RUNNING,
};
const renderMigrationPanelTitle = (migrationStats: RuleMigrationStats) => {
return render(<MigrationPanelTitle migrationStats={migrationStats} />, {
wrapper: TestProviders,
});
};
describe('MigrationPanelTitle', () => {
beforeEach(() => {
useUpdateMigrationMock.mockReturnValue({
mutate: mockUpdateMigration,
isLoading: false,
});
useDeleteMigrationMock.mockReturnValue({
mutate: mockDeleteMigration,
isLoading: false,
});
});
afterEach(() => {
jest.clearAllMocks();
});
describe('Basic rendering', () => {
it('should render migration name correctly', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
expect(screen.getByText('Test Migration')).toBeInTheDocument();
});
it('should render options button', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
expect(screen.getByTestId('openMigrationOptionsButton')).toBeInTheDocument();
});
it('should have correct aria-label for options button', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
expect(screen.getByLabelText(i18n.OPEN_MIGRATION_OPTIONS_BUTTON)).toBeInTheDocument();
});
});
describe('Options menu', () => {
it('should open options menu when button is clicked', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
expect(screen.getByTestId('renameMigrationItem')).toBeInTheDocument();
expect(screen.getByTestId('deleteMigrationItem')).toBeInTheDocument();
});
it('should show rename option in menu', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
expect(screen.getByTestId('renameMigrationItem')).toHaveTextContent(
i18n.RENAME_MIGRATION_TEXT
);
});
it('should show delete option in menu', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
expect(screen.getByTestId('deleteMigrationItem')).toHaveTextContent(i18n.DELETE_BUTTON_TEXT);
});
});
describe('Rename functionality', () => {
it('should enter edit mode when rename is clicked', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const renameButton = screen.getByTestId('renameMigrationItem');
fireEvent.click(renameButton);
expect(screen.getByLabelText('Migration name')).toBeInTheDocument();
});
it('should save new name when edit is confirmed', async () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const renameButton = screen.getByTestId('renameMigrationItem');
fireEvent.click(renameButton);
const input = screen.getByLabelText('Migration name');
fireEvent.change(input, { target: { value: 'New Migration Name' } });
fireEvent.keyDown(input, { key: 'Enter' });
await waitFor(() => {
expect(mockUpdateMigration).toHaveBeenCalledWith({ name: 'New Migration Name' });
});
});
it('should cancel edit when escape is pressed', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const renameButton = screen.getByTestId('renameMigrationItem');
fireEvent.click(renameButton);
const input = screen.getByLabelText('Migration name');
fireEvent.keyDown(input, { key: 'Escape' });
expect(screen.queryByLabelText('Migration name')).not.toBeInTheDocument();
expect(screen.getByText('Test Migration')).toBeInTheDocument();
});
it('should revert name on update error', () => {
useUpdateMigrationMock.mockReturnValue({
mutate: mockUpdateMigration,
isLoading: false,
});
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const renameButton = screen.getByTestId('renameMigrationItem');
fireEvent.click(renameButton);
const input = screen.getByLabelText('Migration name');
fireEvent.change(input, { target: { value: 'New Name' } });
fireEvent.keyDown(input, { key: 'Enter' });
expect(mockUpdateMigration).toHaveBeenCalledWith({ name: 'New Name' });
});
});
describe('Delete functionality', () => {
it('should show delete confirmation modal when delete is clicked', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const deleteButton = screen.getByTestId('deleteMigrationItem');
fireEvent.click(deleteButton);
expect(screen.getByText(i18n.DELETE_MIGRATION_TITLE)).toBeInTheDocument();
expect(screen.getByText(i18n.DELETE_MIGRATION_DESCRIPTION)).toBeInTheDocument();
});
it('should call delete migration when confirmed', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const deleteButton = screen.getByTestId('deleteMigrationItem');
fireEvent.click(deleteButton);
const confirmButton = screen.getByText(i18n.DELETE_MIGRATION_TEXT);
fireEvent.click(confirmButton);
expect(mockDeleteMigration).toHaveBeenCalled();
});
it('should close modal when cancel is clicked', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const deleteButton = screen.getByTestId('deleteMigrationItem');
fireEvent.click(deleteButton);
const cancelButton = screen.getByText(i18n.CANCEL_DELETE_MIGRATION_TEXT);
fireEvent.click(cancelButton);
expect(screen.queryByText(i18n.DELETE_MIGRATION_TITLE)).not.toBeInTheDocument();
});
});
describe('Delete button state based on migration status', () => {
it('should enable delete button for ready migration', () => {
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const deleteButton = screen.getByTestId('deleteMigrationItem');
expect(deleteButton).not.toBeDisabled();
});
it('should disable delete button for running migration', () => {
renderMigrationPanelTitle(mockMigrationStatsRunning);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const deleteButton = screen.getByTestId('deleteMigrationItem');
expect(deleteButton).toBeDisabled();
});
});
describe('Event handling', () => {
it('should prevent event propagation on options button click', () => {
const mockStopPropagation = jest.fn();
const originalStopPropagation = Event.prototype.stopPropagation;
Event.prototype.stopPropagation = mockStopPropagation;
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
expect(mockStopPropagation).toHaveBeenCalled();
Event.prototype.stopPropagation = originalStopPropagation;
});
it('should prevent event propagation on inline edit click', () => {
const mockStopPropagation = jest.fn();
const originalStopPropagation = Event.prototype.stopPropagation;
Event.prototype.stopPropagation = mockStopPropagation;
renderMigrationPanelTitle(mockMigrationStatsReady);
const optionsButton = screen.getByTestId('openMigrationOptionsButton');
fireEvent.click(optionsButton);
const renameButton = screen.getByTestId('renameMigrationItem');
fireEvent.click(renameButton);
const input = screen.getByLabelText('Migration name');
fireEvent.click(input);
expect(mockStopPropagation).toHaveBeenCalled();
Event.prototype.stopPropagation = originalStopPropagation;
});
});
});

View file

@ -8,23 +8,29 @@
import React, { useState, useCallback, useMemo } from 'react';
import {
EuiButtonIcon,
EuiConfirmModal,
EuiContextMenuItem,
EuiContextMenuPanel,
EuiFlexGroup,
EuiFlexItem,
EuiInlineEditText,
EuiPopover,
EuiToolTip,
useEuiTheme,
} from '@elastic/eui';
import { SiemMigrationTaskStatus } from '../../../../../common/siem_migrations/constants';
import { useIsOpenState } from '../../../../common/hooks/use_is_open_state';
import { PanelText } from '../../../../common/components/panel_text';
import { useUpdateMigration } from '../../logic/use_update_migration';
import type { RuleMigrationStats } from '../../types';
import * as i18n from './translations';
import { useDeleteMigration } from '../../logic/use_delete_migration';
interface MigrationPanelTitleProps {
migrationStats: RuleMigrationStats;
}
export const MigrationPanelTitle = React.memo<MigrationPanelTitleProps>(({ migrationStats }) => {
const { euiTheme } = useEuiTheme();
const [name, setName] = useState<string>(migrationStats.name);
const [isEditing, setIsEditing] = useState<boolean>(false);
const {
@ -32,11 +38,19 @@ export const MigrationPanelTitle = React.memo<MigrationPanelTitleProps>(({ migra
close: closePopover,
toggle: togglePopover,
} = useIsOpenState(false);
const {
isOpen: isDeleteModalOpen,
open: openDeleteModal,
close: closeDeleteModal,
} = useIsOpenState(false);
const onRenameError = useCallback(() => {
setName(migrationStats.name); // revert to original name on error. Error toast will be shown by the useUpdateMigration hook
}, [migrationStats.name]);
const { mutate: deleteMigration, isLoading: isDeletingMigration } = useDeleteMigration(
migrationStats.id
);
const { mutate: updateMigration, isLoading: isUpdatingMigrationName } = useUpdateMigration(
migrationStats.id,
{ onError: onRenameError }
@ -55,23 +69,26 @@ export const MigrationPanelTitle = React.memo<MigrationPanelTitleProps>(({ migra
[updateMigration]
);
const items = useMemo(
() => [
<EuiContextMenuItem
key="rename"
onClick={() => {
closePopover();
setIsEditing(true);
}}
icon="pencil"
data-test-subj="renameMigrationItem"
>
{i18n.RENAME_MIGRATION_BUTTON}
</EuiContextMenuItem>,
],
[closePopover, setIsEditing]
const confirmDeleteMigration = useCallback(() => {
deleteMigration();
closeDeleteModal();
}, [deleteMigration, closeDeleteModal]);
const isDeletable = useMemo(
() => migrationStats.status !== SiemMigrationTaskStatus.RUNNING,
[migrationStats.status]
);
const showRename = useCallback(() => {
closePopover();
setIsEditing(true);
}, [closePopover]);
const showDelete = useCallback(() => {
closePopover();
openDeleteModal();
}, [closePopover, openDeleteModal]);
const stopPropagation = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); // prevent click events from bubbling up and toggle the collapsible panel
}, []);
@ -104,7 +121,7 @@ export const MigrationPanelTitle = React.memo<MigrationPanelTitleProps>(({ migra
onClick={togglePopover}
aria-label={i18n.OPEN_MIGRATION_OPTIONS_BUTTON}
data-test-subj="openMigrationOptionsButton"
isLoading={isUpdatingMigrationName}
isLoading={isUpdatingMigrationName || isDeletingMigration}
/>
}
isOpen={isPopoverOpen}
@ -112,8 +129,40 @@ export const MigrationPanelTitle = React.memo<MigrationPanelTitleProps>(({ migra
panelPaddingSize="none"
anchorPosition="downCenter"
>
<EuiContextMenuPanel size="s" items={items} />
<EuiContextMenuPanel size="s">
<EuiContextMenuItem
icon="pencil"
onClick={showRename}
data-test-subj="renameMigrationItem"
>
{i18n.RENAME_MIGRATION_TEXT}
</EuiContextMenuItem>
<EuiContextMenuItem
icon="trash"
onClick={showDelete}
disabled={!isDeletable}
css={{ color: isDeletable ? euiTheme.colors.danger : undefined }}
data-test-subj="deleteMigrationItem"
>
<EuiToolTip content={isDeletable ? undefined : i18n.NOT_DELETABLE_MIGRATION_TEXT}>
<span>{i18n.DELETE_BUTTON_TEXT}</span>
</EuiToolTip>
</EuiContextMenuItem>
</EuiContextMenuPanel>
</EuiPopover>
{isDeleteModalOpen && (
<EuiConfirmModal
title={i18n.DELETE_MIGRATION_TITLE}
onCancel={closeDeleteModal}
onConfirm={confirmDeleteMigration}
confirmButtonText={i18n.DELETE_MIGRATION_TEXT}
cancelButtonText={i18n.CANCEL_DELETE_MIGRATION_TEXT}
buttonColor="danger"
isLoading={isDeletingMigration}
>
<p>{i18n.DELETE_MIGRATION_DESCRIPTION}</p>
</EuiConfirmModal>
)}
</EuiFlexItem>
</>
)}

View file

@ -20,9 +20,9 @@ const useStopMigrationMock = useStopMigration as jest.Mock;
const mockStopMigration = jest.fn();
const inProgressMigrationStats: RuleMigrationStats = {
name: 'test-migration',
status: SiemMigrationTaskStatus.RUNNING,
id: 'c44d2c7d-0de1-4231-8b82-0dcfd67a9fe3',
name: 'test migration',
rules: { total: 26, pending: 6, processing: 10, completed: 9, failed: 1 },
created_at: '2025-05-27T12:12:17.563Z',
last_updated_at: '2025-05-27T12:12:17.563Z',

View file

@ -150,6 +150,42 @@ export const OPEN_MIGRATION_OPTIONS_BUTTON = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.openMigrationOptionsButton',
{ defaultMessage: 'Open migration options' }
);
export const RENAME_MIGRATION_TEXT = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.renameMigrationText',
{ defaultMessage: 'Rename' }
);
export const DELETE_BUTTON_TEXT = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.deleteButtonText',
{ defaultMessage: 'Delete' }
);
export const DELETE_MIGRATION_TEXT = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.deleteMigrationText',
{ defaultMessage: 'Delete Migration' }
);
export const NOT_DELETABLE_MIGRATION_TEXT = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.notDeletableMigrationText',
{ defaultMessage: 'Can not delete running migrations' }
);
export const CANCEL_DELETE_MIGRATION_TEXT = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.cancelDeleteMigrationText',
{ defaultMessage: 'Cancel' }
);
export const DELETE_MIGRATION_TITLE = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.delete.title',
{ defaultMessage: 'Are you sure you want to delete this migration?' }
);
export const DELETE_MIGRATION_DESCRIPTION = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.delete.description',
{
defaultMessage:
'This action cannot be undone. All translations related to this migration will be removed permanently.',
}
);
export const RENAME_MIGRATION_BUTTON = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.panel.renameMigrationButton',
{ defaultMessage: 'Rename' }

View file

@ -68,3 +68,17 @@ export const UPDATE_MIGRATION_NAME_FAILURE = i18n.translate(
defaultMessage: 'Failed to update migration name',
}
);
export const DELETE_MIGRATION_SUCCESS = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.deleteMigrationSuccess',
{
defaultMessage: 'Migration deleted',
}
);
export const DELETE_MIGRATION_FAILURE = i18n.translate(
'xpack.securitySolution.siemMigrations.rules.deleteMigrationFailDescription',
{
defaultMessage: 'Failed to delete migration',
}
);

View file

@ -0,0 +1,30 @@
/*
* 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 { useMutation } from '@tanstack/react-query';
import { useKibana } from '../../../common/lib/kibana/kibana_react';
import { SIEM_RULE_MIGRATION_PATH } from '../../../../common/siem_migrations/constants';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
import * as i18n from './translations';
export const DELETE_MIGRATION_RULE_MUTATION_KEY = ['DELETE', SIEM_RULE_MIGRATION_PATH];
export const useDeleteMigration = (migrationId: string) => {
const { addError, addSuccess } = useAppToasts();
const rulesMigrationService = useKibana().services.siemMigrations.rules;
return useMutation({
mutationFn: () => rulesMigrationService.deleteMigration(migrationId),
mutationKey: DELETE_MIGRATION_RULE_MUTATION_KEY,
onSuccess: () => {
addSuccess(i18n.DELETE_MIGRATION_SUCCESS);
},
onError: (error) => {
addError(error, { title: i18n.DELETE_MIGRATION_FAILURE });
},
});
};

View file

@ -158,6 +158,22 @@ export class SiemRulesMigrationsService {
}
}
/** Deletes a rule migration by its ID, refreshing the stats to remove it from the list */
public async deleteMigration(migrationId: string): Promise<string> {
try {
await api.deleteMigration({ migrationId });
// Refresh stats to remove the deleted migration from the list. All UI observables will be updated automatically
await this.getRuleMigrationsStats();
this.telemetry.reportSetupMigrationDeleted({ migrationId });
return migrationId;
} catch (error) {
this.telemetry.reportSetupMigrationDeleted({ migrationId, error });
throw error;
}
}
/** Upserts resources for a rule migration, batching the requests to avoid hitting the max payload size limit of the API */
public async upsertMigrationResources(
migrationId: string,

View file

@ -68,6 +68,15 @@ export class SiemRulesMigrationsTelemetry {
});
};
reportSetupMigrationDeleted = (params: { migrationId: string; error?: Error }) => {
const { migrationId, error } = params;
this.telemetryService.reportEvent(SiemMigrationsEventTypes.SetupMigrationDeleted, {
eventName: siemMigrationEventNames[SiemMigrationsEventTypes.SetupMigrationDeleted],
migrationId,
...this.getBaseResultParams(error),
});
};
reportSetupResourceUploaded = (params: {
migrationId: string;
type: RuleMigrationResourceType;