mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Actionable Observability] [ResponseOps] Remove view rule details link from the alerts table in the rule details page (#137576)
* Remove view rule details link in rule details page * Renaming and bringing back eslint comment * Rename id to pageId * Add missing id in AlertsTableFlyoutBaseProps interface * Fixing the types to match reality. Not everything is string[] * Splitting ObservabilityActions from table t-grid; adding tests for AlertsFlyoutBody and ObservabilityActions * Fixing more mappings * removing unnecessary reduce * Changing to unknown type instead of listing all the possible options to match the ES Typescript interface. * reverting all the type changes * reverting all the type changes * Forcing the fixture data to be TimelineNonEcsData[] * Aliasing id to pageId Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Chris Cowan <chris@elastic.co>
This commit is contained in:
parent
02e92387cb
commit
7f46fde188
16 changed files with 488 additions and 199 deletions
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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 { render } from '../../../../utils/test_helper';
|
||||
import React from 'react';
|
||||
import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting';
|
||||
import { createObservabilityRuleTypeRegistryMock } from '../../../../rules/observability_rule_type_registry_mock';
|
||||
import AlertsFlyoutBody from './alerts_flyout_body';
|
||||
import { inventoryThresholdAlert } from '../../../../rules/fixtures/example_alerts';
|
||||
import { parseAlert } from '../parse_alert';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../../rule_details/types';
|
||||
|
||||
describe('AlertsFlyoutBody', () => {
|
||||
jest
|
||||
.spyOn(useUiSettingHook, 'useUiSetting')
|
||||
.mockImplementation(() => 'MMM D, YYYY @ HH:mm:ss.SSS');
|
||||
const observabilityRuleTypeRegistryMock = createObservabilityRuleTypeRegistryMock();
|
||||
|
||||
const setup = (id: string) => {
|
||||
const dataFieldEs = inventoryThresholdAlert.reduce(
|
||||
(acc, d) => ({ ...acc, [d.field]: d.value }),
|
||||
{}
|
||||
);
|
||||
const alert = parseAlert(observabilityRuleTypeRegistryMock)(dataFieldEs);
|
||||
return render(<AlertsFlyoutBody alert={alert} id={id} />);
|
||||
};
|
||||
|
||||
it('should render View rule detail link', async () => {
|
||||
const flyout = setup('test');
|
||||
expect(flyout.getByTestId('viewRuleDetailsFlyout')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should NOT render View rule detail link for RULE_DETAILS_PAGE_ID', async () => {
|
||||
const flyout = setup(RULE_DETAILS_PAGE_ID);
|
||||
expect(flyout.queryByTestId('viewRuleDetailsFlyout')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -26,20 +26,23 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
import moment from 'moment-timezone';
|
||||
import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../../rule_details/types';
|
||||
import { asDuration } from '../../../../../common/utils/formatters';
|
||||
import { translations, paths } from '../../../../config';
|
||||
import { AlertStatusIndicator } from '../../../../components/shared/alert_status_indicator';
|
||||
import { FlyoutProps } from './types';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function AlertsFlyoutBody(props: FlyoutProps) {
|
||||
const alert = props.alert;
|
||||
export default function AlertsFlyoutBody({ alert, id: pageId }: FlyoutProps) {
|
||||
const { services } = useKibana();
|
||||
const { http } = services;
|
||||
const dateFormat = useUiSetting<string>('dateFormat');
|
||||
const prepend = http?.basePath.prepend;
|
||||
const ruleId = get(props.alert.fields, ALERT_RULE_UUID) ?? null;
|
||||
const linkToRule = ruleId && prepend ? prepend(paths.observability.ruleDetails(ruleId)) : null;
|
||||
const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
|
||||
const linkToRule =
|
||||
pageId !== RULE_DETAILS_PAGE_ID && ruleId && prepend
|
||||
? prepend(paths.observability.ruleDetails(ruleId))
|
||||
: null;
|
||||
const overviewListItems = [
|
||||
{
|
||||
title: translations.alertsFlyout.statusLabel,
|
||||
|
|
|
@ -24,7 +24,7 @@ export const useToGetInternalFlyout = (
|
|||
const alert = parseAlert(observabilityRuleTypeRegistry)(
|
||||
props.alert as unknown as Record<string, unknown>
|
||||
);
|
||||
return <AlertsFlyoutBody alert={alert} />;
|
||||
return <AlertsFlyoutBody alert={alert} id={props.id} />;
|
||||
},
|
||||
[observabilityRuleTypeRegistry]
|
||||
);
|
||||
|
|
|
@ -9,4 +9,5 @@ import { TopAlert } from '../../containers';
|
|||
|
||||
export interface FlyoutProps {
|
||||
alert: TopAlert;
|
||||
id?: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 { act } from '@testing-library/react-hooks';
|
||||
import { kibanaStartMock } from '../../../utils/kibana_react.mock';
|
||||
import React from 'react';
|
||||
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
|
||||
import { ObservabilityActions, ObservabilityActionsProps } from './observability_actions';
|
||||
import { inventoryThresholdAlert } from '../../../rules/fixtures/example_alerts';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types';
|
||||
import { createObservabilityRuleTypeRegistryMock } from '../../../rules/observability_rule_type_registry_mock';
|
||||
import { TimelineNonEcsData } from '@kbn/timelines-plugin/common';
|
||||
|
||||
const mockUseKibanaReturnValue = kibanaStartMock.startContract();
|
||||
|
||||
jest.mock('../../../utils/kibana_react', () => ({
|
||||
__esModule: true,
|
||||
useKibana: jest.fn(() => mockUseKibanaReturnValue),
|
||||
}));
|
||||
|
||||
jest.mock('../../../hooks/use_get_user_cases_permissions', () => ({
|
||||
useGetUserCasesPermissions: jest.fn(() => ({})),
|
||||
}));
|
||||
|
||||
describe('ObservabilityActions component', () => {
|
||||
const setup = async (pageId: string) => {
|
||||
const props: ObservabilityActionsProps = {
|
||||
eventId: '6d4c6d74-d51a-495c-897d-88ced3b95e30',
|
||||
ecsData: {
|
||||
_id: '6d4c6d74-d51a-495c-897d-88ced3b95e30',
|
||||
_index: '.internal.alerts-observability.metrics.alerts-default-000001',
|
||||
},
|
||||
data: inventoryThresholdAlert as unknown as TimelineNonEcsData[],
|
||||
observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(),
|
||||
setEventsDeleted: jest.fn(),
|
||||
setFlyoutAlert: jest.fn(),
|
||||
id: pageId,
|
||||
};
|
||||
|
||||
const wrapper = mountWithIntl(<ObservabilityActions {...props} />);
|
||||
await act(async () => {
|
||||
await nextTick();
|
||||
wrapper.update();
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
it('should hide "View rule details" menu item for rule page id', async () => {
|
||||
const wrapper = await setup(RULE_DETAILS_PAGE_ID);
|
||||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0);
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show "View rule details" menu item', async () => {
|
||||
const wrapper = await setup('nothing');
|
||||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetails"]').hostNodes().length).toBe(1);
|
||||
});
|
||||
|
||||
it('should create a valid link for rule details page', async () => {
|
||||
const wrapper = await setup('nothing');
|
||||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().prop('href')).toBe(
|
||||
'/app/observability/alerts/rules/06f53080-0f91-11ed-9d86-013908b232ef'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiButtonIcon,
|
||||
EuiFlexItem,
|
||||
EuiContextMenuItem,
|
||||
EuiContextMenuPanel,
|
||||
EuiPopover,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
|
||||
import { CaseAttachments } from '@kbn/cases-plugin/public';
|
||||
import { CommentType } from '@kbn/cases-plugin/common';
|
||||
import type { ActionProps } from '@kbn/timelines-plugin/common';
|
||||
import { useKibana } from '../../../utils/kibana_react';
|
||||
import { observabilityFeatureId } from '../../../../common';
|
||||
import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions';
|
||||
import { parseAlert } from './parse_alert';
|
||||
import { translations, paths } from '../../../config';
|
||||
import {
|
||||
ADD_TO_EXISTING_CASE,
|
||||
ADD_TO_NEW_CASE,
|
||||
} from '../containers/alerts_table_t_grid/translations';
|
||||
import { ObservabilityAppServices } from '../../../application/types';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/types';
|
||||
import type { TopAlert } from '../containers/alerts_page/alerts_page';
|
||||
import { ObservabilityRuleTypeRegistry } from '../../..';
|
||||
|
||||
export type ObservabilityActionsProps = Pick<
|
||||
ActionProps,
|
||||
'data' | 'eventId' | 'ecsData' | 'setEventsDeleted'
|
||||
> & {
|
||||
setFlyoutAlert: React.Dispatch<React.SetStateAction<TopAlert | undefined>>;
|
||||
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export function ObservabilityActions({
|
||||
data,
|
||||
eventId,
|
||||
ecsData,
|
||||
id: pageId,
|
||||
observabilityRuleTypeRegistry,
|
||||
setFlyoutAlert,
|
||||
}: ObservabilityActionsProps) {
|
||||
const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {});
|
||||
const [openActionsPopoverId, setActionsPopover] = useState(null);
|
||||
const { cases, http } = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
const parseObservabilityAlert = useMemo(
|
||||
() => parseAlert(observabilityRuleTypeRegistry),
|
||||
[observabilityRuleTypeRegistry]
|
||||
);
|
||||
|
||||
const alert = parseObservabilityAlert(dataFieldEs);
|
||||
|
||||
const closeActionsPopover = useCallback(() => {
|
||||
setActionsPopover(null);
|
||||
}, []);
|
||||
|
||||
const toggleActionsPopover = useCallback((id) => {
|
||||
setActionsPopover((current) => (current ? null : id));
|
||||
}, []);
|
||||
|
||||
const userCasesPermissions = useGetUserCasesPermissions();
|
||||
const ruleId = alert.fields['kibana.alert.rule.uuid'] ?? null;
|
||||
const linkToRule =
|
||||
pageId !== RULE_DETAILS_PAGE_ID && ruleId
|
||||
? http.basePath.prepend(paths.observability.ruleDetails(ruleId))
|
||||
: null;
|
||||
const caseAttachments: CaseAttachments = useMemo(() => {
|
||||
return ecsData?._id
|
||||
? [
|
||||
{
|
||||
alertId: ecsData?._id ?? '',
|
||||
index: ecsData?._index ?? '',
|
||||
owner: observabilityFeatureId,
|
||||
type: CommentType.alert,
|
||||
rule: cases.helpers.getRuleIdFromEvent({ ecs: ecsData, data: data ?? [] }),
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}, [ecsData, cases.helpers, data]);
|
||||
|
||||
const createCaseFlyout = cases.hooks.getUseCasesAddToNewCaseFlyout();
|
||||
|
||||
const selectCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal();
|
||||
|
||||
const handleAddToNewCaseClick = useCallback(() => {
|
||||
createCaseFlyout.open({ attachments: caseAttachments });
|
||||
closeActionsPopover();
|
||||
}, [createCaseFlyout, caseAttachments, closeActionsPopover]);
|
||||
|
||||
const handleAddToExistingCaseClick = useCallback(() => {
|
||||
selectCaseModal.open({ attachments: caseAttachments });
|
||||
closeActionsPopover();
|
||||
}, [caseAttachments, closeActionsPopover, selectCaseModal]);
|
||||
|
||||
const actionsMenuItems = useMemo(() => {
|
||||
return [
|
||||
...(userCasesPermissions.create && userCasesPermissions.read
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="add-to-existing-case-action"
|
||||
onClick={handleAddToExistingCaseClick}
|
||||
size="s"
|
||||
>
|
||||
{ADD_TO_EXISTING_CASE}
|
||||
</EuiContextMenuItem>,
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="add-to-new-case-action"
|
||||
onClick={handleAddToNewCaseClick}
|
||||
size="s"
|
||||
>
|
||||
{ADD_TO_NEW_CASE}
|
||||
</EuiContextMenuItem>,
|
||||
]
|
||||
: []),
|
||||
|
||||
...(!!linkToRule
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
key="viewRuleDetails"
|
||||
data-test-subj="viewRuleDetails"
|
||||
href={linkToRule}
|
||||
>
|
||||
{translations.alertsTable.viewRuleDetailsButtonText}
|
||||
</EuiContextMenuItem>,
|
||||
]
|
||||
: []),
|
||||
|
||||
...[
|
||||
<EuiContextMenuItem
|
||||
key="viewAlertDetails"
|
||||
data-test-subj="viewAlertDetails"
|
||||
onClick={() => {
|
||||
closeActionsPopover();
|
||||
setFlyoutAlert(alert);
|
||||
}}
|
||||
>
|
||||
{translations.alertsTable.viewAlertDetailsButtonText}
|
||||
</EuiContextMenuItem>,
|
||||
],
|
||||
];
|
||||
}, [
|
||||
userCasesPermissions.create,
|
||||
userCasesPermissions.read,
|
||||
handleAddToExistingCaseClick,
|
||||
handleAddToNewCaseClick,
|
||||
linkToRule,
|
||||
alert,
|
||||
setFlyoutAlert,
|
||||
closeActionsPopover,
|
||||
]);
|
||||
|
||||
const actionsToolTip =
|
||||
actionsMenuItems.length <= 0
|
||||
? translations.alertsTable.notEnoughPermissions
|
||||
: translations.alertsTable.moreActionsTextLabel;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexItem>
|
||||
<EuiToolTip content={translations.alertsTable.viewInAppTextLabel}>
|
||||
<EuiButtonIcon
|
||||
size="s"
|
||||
href={http.basePath.prepend(alert.link ?? '')}
|
||||
iconType="eye"
|
||||
color="text"
|
||||
aria-label={translations.alertsTable.viewInAppTextLabel}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiToolTip content={actionsToolTip}>
|
||||
<EuiButtonIcon
|
||||
display="empty"
|
||||
size="s"
|
||||
color="text"
|
||||
iconType="boxesHorizontal"
|
||||
aria-label={actionsToolTip}
|
||||
onClick={() => toggleActionsPopover(eventId)}
|
||||
data-test-subj="alertsTableRowActionMore"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
}
|
||||
isOpen={openActionsPopoverId === eventId}
|
||||
closePopover={closeActionsPopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -23,16 +23,7 @@ import {
|
|||
ALERT_START,
|
||||
} from '@kbn/rule-data-utils';
|
||||
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiDataGridColumn,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiContextMenuItem,
|
||||
EuiContextMenuPanel,
|
||||
EuiPopover,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { EuiDataGridColumn, EuiFlexGroup } from '@elastic/eui';
|
||||
|
||||
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
|
||||
|
@ -53,8 +44,6 @@ import type {
|
|||
ControlColumnProps,
|
||||
RowRenderer,
|
||||
} from '@kbn/timelines-plugin/common';
|
||||
import { CaseAttachments } from '@kbn/cases-plugin/public';
|
||||
import { CommentType } from '@kbn/cases-plugin/common';
|
||||
import { getAlertsPermissions } from '../../../../hooks/use_alert_permission';
|
||||
|
||||
import type { TopAlert } from '../alerts_page/alerts_page';
|
||||
|
@ -63,13 +52,15 @@ import { getRenderCellValue } from '../../components/render_cell_value';
|
|||
import { observabilityAppId, observabilityFeatureId } from '../../../../../common';
|
||||
import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions';
|
||||
import { usePluginContext } from '../../../../hooks/use_plugin_context';
|
||||
import { LazyAlertsFlyout, ObservabilityRuleTypeRegistry } from '../../../..';
|
||||
import { parseAlert } from '../../components/parse_alert';
|
||||
import { translations, paths } from '../../../../config';
|
||||
import { LazyAlertsFlyout } from '../../../..';
|
||||
import { translations } from '../../../../config';
|
||||
import { addDisplayNames } from './add_display_names';
|
||||
import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from './translations';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
import { useBulkAddToCaseActions } from '../../../../hooks/use_alert_bulk_case_actions';
|
||||
import {
|
||||
ObservabilityActions,
|
||||
ObservabilityActionsProps,
|
||||
} from '../../components/observability_actions';
|
||||
|
||||
interface AlertsTableTGridProps {
|
||||
indexNames: string[];
|
||||
|
@ -82,14 +73,6 @@ interface AlertsTableTGridProps {
|
|||
itemsPerPage?: number;
|
||||
}
|
||||
|
||||
export type ObservabilityActionsProps = Pick<
|
||||
ActionProps,
|
||||
'data' | 'eventId' | 'ecsData' | 'setEventsDeleted'
|
||||
> & {
|
||||
setFlyoutAlert: React.Dispatch<React.SetStateAction<TopAlert | undefined>>;
|
||||
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry;
|
||||
};
|
||||
|
||||
const EventsThContent = styled.div.attrs(({ className = '' }) => ({
|
||||
className: `siemEventsTable__thContent ${className}`,
|
||||
}))<{ textAlign?: string; width?: number }>`
|
||||
|
@ -146,164 +129,6 @@ const NO_ROW_RENDER: RowRenderer[] = [];
|
|||
|
||||
const trailingControlColumns: never[] = [];
|
||||
|
||||
export function ObservabilityActions({
|
||||
data,
|
||||
eventId,
|
||||
ecsData,
|
||||
observabilityRuleTypeRegistry,
|
||||
setFlyoutAlert,
|
||||
}: ObservabilityActionsProps) {
|
||||
const dataFieldEs = data.reduce((acc, d) => ({ ...acc, [d.field]: d.value }), {});
|
||||
const [openActionsPopoverId, setActionsPopover] = useState(null);
|
||||
const { cases, http } = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
const parseObservabilityAlert = useMemo(
|
||||
() => parseAlert(observabilityRuleTypeRegistry),
|
||||
[observabilityRuleTypeRegistry]
|
||||
);
|
||||
|
||||
const alert = parseObservabilityAlert(dataFieldEs);
|
||||
|
||||
const closeActionsPopover = useCallback(() => {
|
||||
setActionsPopover(null);
|
||||
}, []);
|
||||
|
||||
const toggleActionsPopover = useCallback((id) => {
|
||||
setActionsPopover((current) => (current ? null : id));
|
||||
}, []);
|
||||
|
||||
const userCasesPermissions = useGetUserCasesPermissions();
|
||||
const ruleId = alert.fields['kibana.alert.rule.uuid'] ?? null;
|
||||
const linkToRule = ruleId ? http.basePath.prepend(paths.observability.ruleDetails(ruleId)) : null;
|
||||
const caseAttachments: CaseAttachments = useMemo(() => {
|
||||
return ecsData?._id
|
||||
? [
|
||||
{
|
||||
alertId: ecsData?._id ?? '',
|
||||
index: ecsData?._index ?? '',
|
||||
owner: observabilityFeatureId,
|
||||
type: CommentType.alert,
|
||||
rule: cases.helpers.getRuleIdFromEvent({ ecs: ecsData, data: data ?? [] }),
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}, [ecsData, cases.helpers, data]);
|
||||
|
||||
const createCaseFlyout = cases.hooks.getUseCasesAddToNewCaseFlyout();
|
||||
|
||||
const selectCaseModal = cases.hooks.getUseCasesAddToExistingCaseModal();
|
||||
|
||||
const handleAddToNewCaseClick = useCallback(() => {
|
||||
createCaseFlyout.open({ attachments: caseAttachments });
|
||||
closeActionsPopover();
|
||||
}, [createCaseFlyout, caseAttachments, closeActionsPopover]);
|
||||
|
||||
const handleAddToExistingCaseClick = useCallback(() => {
|
||||
selectCaseModal.open({ attachments: caseAttachments });
|
||||
closeActionsPopover();
|
||||
}, [caseAttachments, closeActionsPopover, selectCaseModal]);
|
||||
|
||||
const actionsMenuItems = useMemo(() => {
|
||||
return [
|
||||
...(userCasesPermissions.create && userCasesPermissions.read
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="add-to-existing-case-action"
|
||||
onClick={handleAddToExistingCaseClick}
|
||||
size="s"
|
||||
>
|
||||
{ADD_TO_EXISTING_CASE}
|
||||
</EuiContextMenuItem>,
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="add-to-new-case-action"
|
||||
onClick={handleAddToNewCaseClick}
|
||||
size="s"
|
||||
>
|
||||
{ADD_TO_NEW_CASE}
|
||||
</EuiContextMenuItem>,
|
||||
]
|
||||
: []),
|
||||
|
||||
...(!!linkToRule
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
key="viewRuleDetails"
|
||||
data-test-subj="viewRuleDetails"
|
||||
href={linkToRule}
|
||||
>
|
||||
{translations.alertsTable.viewRuleDetailsButtonText}
|
||||
</EuiContextMenuItem>,
|
||||
]
|
||||
: []),
|
||||
|
||||
...[
|
||||
<EuiContextMenuItem
|
||||
key="viewAlertDetails"
|
||||
data-test-subj="viewAlertDetails"
|
||||
onClick={() => {
|
||||
closeActionsPopover();
|
||||
setFlyoutAlert(alert);
|
||||
}}
|
||||
>
|
||||
{translations.alertsTable.viewAlertDetailsButtonText}
|
||||
</EuiContextMenuItem>,
|
||||
],
|
||||
];
|
||||
}, [
|
||||
userCasesPermissions.create,
|
||||
userCasesPermissions.read,
|
||||
handleAddToExistingCaseClick,
|
||||
handleAddToNewCaseClick,
|
||||
linkToRule,
|
||||
alert,
|
||||
setFlyoutAlert,
|
||||
closeActionsPopover,
|
||||
]);
|
||||
|
||||
const actionsToolTip =
|
||||
actionsMenuItems.length <= 0
|
||||
? translations.alertsTable.notEnoughPermissions
|
||||
: translations.alertsTable.moreActionsTextLabel;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexItem>
|
||||
<EuiToolTip content={translations.alertsTable.viewInAppTextLabel}>
|
||||
<EuiButtonIcon
|
||||
size="s"
|
||||
href={http.basePath.prepend(alert.link ?? '')}
|
||||
iconType="eye"
|
||||
color="text"
|
||||
aria-label={translations.alertsTable.viewInAppTextLabel}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiToolTip content={actionsToolTip}>
|
||||
<EuiButtonIcon
|
||||
display="empty"
|
||||
size="s"
|
||||
color="text"
|
||||
iconType="boxesHorizontal"
|
||||
aria-label={actionsToolTip}
|
||||
onClick={() => toggleActionsPopover(eventId)}
|
||||
data-test-subj="alertsTableRowActionMore"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
}
|
||||
isOpen={openActionsPopoverId === eventId}
|
||||
closePopover={closeActionsPopover}
|
||||
panelPaddingSize="none"
|
||||
anchorPosition="downLeft"
|
||||
>
|
||||
<EuiContextMenuPanel size="s" items={actionsMenuItems} />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
const FIELDS_WITHOUT_CELL_ACTIONS = [
|
||||
'@timestamp',
|
||||
'signal.rule.risk_score',
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
import React from 'react';
|
||||
import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy';
|
||||
import { ObservabilityRuleTypeRegistry } from '../../../../rules/create_observability_rule_type_registry';
|
||||
import { ObservabilityActions } from './alerts_table_t_grid';
|
||||
import type { ObservabilityActionsProps } from './alerts_table_t_grid';
|
||||
import { ObservabilityActions } from '../../components/observability_actions';
|
||||
import type { ObservabilityActionsProps } from '../../components/observability_actions';
|
||||
|
||||
const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data'] => {
|
||||
return Object.entries(alerts).reduce<ObservabilityActionsProps['data']>(
|
||||
|
@ -19,12 +19,17 @@ const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data']
|
|||
const fakeSetEventsDeleted = () => [];
|
||||
export const getRowActions = (observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry) => {
|
||||
return () => ({
|
||||
renderCustomActionsRow: (alert: EcsFieldsResponse, setFlyoutAlert: (data: unknown) => void) => {
|
||||
renderCustomActionsRow: (
|
||||
alert: EcsFieldsResponse,
|
||||
setFlyoutAlert: (data: unknown, id?: string) => void,
|
||||
id?: string
|
||||
) => {
|
||||
return (
|
||||
<ObservabilityActions
|
||||
data={buildData(alert)}
|
||||
eventId={alert._id}
|
||||
ecsData={{ _id: alert._id, _index: alert._index }}
|
||||
id={id}
|
||||
observabilityRuleTypeRegistry={observabilityRuleTypeRegistry}
|
||||
setEventsDeleted={fakeSetEventsDeleted}
|
||||
setFlyoutAlert={setFlyoutAlert}
|
||||
|
|
|
@ -37,7 +37,12 @@ import { RuleDefinitionProps } from '@kbn/triggers-actions-ui-plugin/public';
|
|||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { DeleteModalConfirmation } from './components/delete_modal_confirmation';
|
||||
import { CenterJustifiedSpinner } from './components/center_justified_spinner';
|
||||
import { RuleDetailsPathParams, EVENT_LOG_LIST_TAB, ALERT_LIST_TAB } from './types';
|
||||
import {
|
||||
RuleDetailsPathParams,
|
||||
EVENT_LOG_LIST_TAB,
|
||||
ALERT_LIST_TAB,
|
||||
RULE_DETAILS_PAGE_ID,
|
||||
} from './types';
|
||||
import { useBreadcrumbs } from '../../hooks/use_breadcrumbs';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { useFetchRule } from '../../hooks/use_fetch_rule';
|
||||
|
@ -155,7 +160,7 @@ export function RuleDetailsPage() {
|
|||
const alertStateProps = {
|
||||
alertsTableConfigurationRegistry,
|
||||
configurationId: observabilityFeatureId,
|
||||
id: `case-details-alerts-o11y`,
|
||||
id: RULE_DETAILS_PAGE_ID,
|
||||
flyoutSize: 's' as EuiFlyoutSize,
|
||||
featureIds: [features] as AlertConsumers[],
|
||||
query: {
|
||||
|
|
|
@ -80,3 +80,4 @@ export interface ActionsProps {
|
|||
export const EVENT_LOG_LIST_TAB = 'rule_event_log_list';
|
||||
export const ALERT_LIST_TAB = 'rule_alert_list';
|
||||
export const EVENT_ERROR_LOG_TAB = 'rule_error_log_list';
|
||||
export const RULE_DETAILS_PAGE_ID = 'rule-details-alerts-o11y';
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const inventoryThresholdAlert = [
|
||||
{
|
||||
field: 'kibana.alert.workflow_status',
|
||||
value: ['open'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.status',
|
||||
value: ['active'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.uuid',
|
||||
value: ['06f53080-0f91-11ed-9d86-013908b232ef'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.reason',
|
||||
value: ['CPU usage is 106.5% in the last 1 min for host-0. Alert when > 1%.'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.producer',
|
||||
value: ['infrastructure'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.consumer',
|
||||
value: ['alerts'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.category',
|
||||
value: ['Inventory'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.start',
|
||||
value: ['2022-07-29T22:51:51.904Z'],
|
||||
},
|
||||
{
|
||||
field: 'event.action',
|
||||
value: ['open'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.rule_type_id',
|
||||
value: ['metrics.alert.inventory.threshold'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.duration.us',
|
||||
value: [0],
|
||||
},
|
||||
{
|
||||
field: '@timestamp',
|
||||
value: ['2022-07-29T22:51:51.904Z'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.instance.id',
|
||||
value: ['host-0'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.name',
|
||||
value: ['Test Alert'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.space_ids',
|
||||
value: ['default'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.uuid',
|
||||
value: ['6d4c6d74-d51a-495c-897d-88ced3b95e30'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.execution.uuid',
|
||||
value: ['b8cccff3-2d3e-41d0-a91e-6aae8efce8ba'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.version',
|
||||
value: ['8.5.0'],
|
||||
},
|
||||
{
|
||||
field: 'event.kind',
|
||||
value: ['signal'],
|
||||
},
|
||||
{
|
||||
field: 'kibana.alert.rule.parameters',
|
||||
value: [
|
||||
{
|
||||
sourceId: 'default',
|
||||
criteria: [
|
||||
{
|
||||
comparator: '>',
|
||||
timeSize: 1,
|
||||
metric: 'cpu',
|
||||
threshold: [1],
|
||||
customMetric: {
|
||||
field: '',
|
||||
aggregation: 'avg',
|
||||
id: 'alert-custom-metric',
|
||||
type: 'custom',
|
||||
},
|
||||
timeUnit: 'm',
|
||||
},
|
||||
],
|
||||
nodeType: 'host',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
field: '_id',
|
||||
value: '6d4c6d74-d51a-495c-897d-88ced3b95e30',
|
||||
},
|
||||
{
|
||||
field: '_index',
|
||||
value: '.internal.alerts-observability.metrics.alerts-default-000001',
|
||||
},
|
||||
];
|
|
@ -10,10 +10,12 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
|
|||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { ObservabilityPublicPluginsStart } from '../plugin';
|
||||
|
||||
export type StartServices = CoreStart &
|
||||
ObservabilityPublicPluginsStart & {
|
||||
export type StartServices<AdditionalServices extends object = {}> = CoreStart &
|
||||
ObservabilityPublicPluginsStart &
|
||||
AdditionalServices & {
|
||||
storage: Storage;
|
||||
};
|
||||
const useTypedKibana = () => useKibana<StartServices>();
|
||||
const useTypedKibana = <AdditionalServices extends object = {}>() =>
|
||||
useKibana<StartServices<AdditionalServices>>();
|
||||
|
||||
export { useTypedKibana as useKibana };
|
||||
|
|
|
@ -36,6 +36,7 @@ interface AlertsFlyoutProps {
|
|||
isLoading: boolean;
|
||||
onClose: () => void;
|
||||
onPaginate: (pageIndex: number) => void;
|
||||
id?: string;
|
||||
}
|
||||
export const AlertsFlyout: React.FunctionComponent<AlertsFlyoutProps> = ({
|
||||
alert,
|
||||
|
@ -46,6 +47,7 @@ export const AlertsFlyout: React.FunctionComponent<AlertsFlyoutProps> = ({
|
|||
isLoading,
|
||||
onClose,
|
||||
onPaginate,
|
||||
id,
|
||||
}: AlertsFlyoutProps) => {
|
||||
const {
|
||||
header: Header,
|
||||
|
@ -60,9 +62,10 @@ export const AlertsFlyout: React.FunctionComponent<AlertsFlyoutProps> = ({
|
|||
const passedProps = useMemo(
|
||||
() => ({
|
||||
alert,
|
||||
id,
|
||||
isLoading,
|
||||
}),
|
||||
[alert, isLoading]
|
||||
[alert, id, isLoading]
|
||||
);
|
||||
|
||||
const FlyoutBody = useCallback(
|
||||
|
|
|
@ -156,7 +156,7 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
{renderCustomActionsRow &&
|
||||
renderCustomActionsRow(alerts[visibleRowIndex], handleFlyoutAlert)}
|
||||
renderCustomActionsRow(alerts[visibleRowIndex], handleFlyoutAlert, props.id)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
},
|
||||
|
@ -173,9 +173,10 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
|||
}, [
|
||||
actionsColumnWidth,
|
||||
alerts,
|
||||
handleFlyoutAlert,
|
||||
getBulkActionsLeadingControlColumn,
|
||||
handleFlyoutAlert,
|
||||
isBulkActionsColumnActive,
|
||||
props.id,
|
||||
props.leadingControlColumns,
|
||||
props.showExpandToDetails,
|
||||
renderCustomActionsRow,
|
||||
|
@ -242,6 +243,7 @@ const AlertsTable: React.FunctionComponent<AlertsTableProps> = (props: AlertsTab
|
|||
flyoutIndex={flyoutAlertIndex + pagination.pageIndex * pagination.pageSize}
|
||||
onPaginate={onPaginateFlyout}
|
||||
isLoading={isLoading}
|
||||
id={props.id}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
|
|
|
@ -245,6 +245,7 @@ const AlertsTableState = ({
|
|||
flyoutSize,
|
||||
pageSize: pagination.pageSize,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
id,
|
||||
leadingControlColumns: [],
|
||||
showExpandToDetails,
|
||||
trailingControlColumns: [],
|
||||
|
@ -258,6 +259,7 @@ const AlertsTableState = ({
|
|||
columns,
|
||||
flyoutSize,
|
||||
pagination.pageSize,
|
||||
id,
|
||||
showExpandToDetails,
|
||||
useFetchAlertsData,
|
||||
updatedAt,
|
||||
|
|
|
@ -419,6 +419,7 @@ export interface AlertsTableProps {
|
|||
flyoutSize?: EuiFlyoutSize;
|
||||
pageSize: number;
|
||||
pageSizeOptions: number[];
|
||||
id?: string;
|
||||
leadingControlColumns: EuiDataGridControlColumn[];
|
||||
showExpandToDetails: boolean;
|
||||
trailingControlColumns: EuiDataGridControlColumn[];
|
||||
|
@ -443,6 +444,7 @@ export type AlertTableFlyoutComponent =
|
|||
export interface AlertsTableFlyoutBaseProps {
|
||||
alert: EcsFieldsResponse;
|
||||
isLoading: boolean;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface BulkActionsConfig {
|
||||
|
@ -470,7 +472,8 @@ export interface AlertsTableConfigurationRegistry {
|
|||
useActionsColumn?: () => {
|
||||
renderCustomActionsRow: (
|
||||
alert: EcsFieldsResponse,
|
||||
setFlyoutAlert: (data: unknown) => void
|
||||
setFlyoutAlert: (data: unknown) => void,
|
||||
id?: string
|
||||
) => JSX.Element;
|
||||
width?: number;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue