mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[8.15] [Security Solutions] Add a preview button to alerts inside the risk contribution panel (#187148) (#188219)
# Backport This will backport the following commits from `main` to `8.15`: - [[Security Solutions] Add a preview button to alerts inside the risk contribution panel (#187148)](https://github.com/elastic/kibana/pull/187148) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Pablo Machado","email":"pablo.nevesmachado@elastic.co"},"sourceCommit":{"committedDate":"2024-07-09T09:54:47Z","message":"[Security Solutions] Add a preview button to alerts inside the risk contribution panel (#187148)\n\n## Summary\r\n\r\nThe feature is hidden behind the flag `entityAlertPreviewEnabled`\r\n\r\n* It adds the extra column to the risk contribution panel with a button\r\nthat opens the alert preview panel\r\n\r\n\r\n\r\n0677de6b
-a6fa-461b-92b5-188d79e7274c\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] Any UI touched in this PR is usable by keyboard only (learn more\r\nabout [keyboard accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] This renders correctly on smaller devices using a responsive\r\nlayout. (You can test this [in your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))","sha":"0ee7a7a76d8eefc5ab44e0320a44edc178746212","branchLabelMapping":{"^v8.16.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","backport:skip","Theme: entity_analytics","v8.16.0"],"number":187148,"url":"https://github.com/elastic/kibana/pull/187148","mergeCommit":{"message":"[Security Solutions] Add a preview button to alerts inside the risk contribution panel (#187148)\n\n## Summary\r\n\r\nThe feature is hidden behind the flag `entityAlertPreviewEnabled`\r\n\r\n* It adds the extra column to the risk contribution panel with a button\r\nthat opens the alert preview panel\r\n\r\n\r\n\r\n0677de6b
-a6fa-461b-92b5-188d79e7274c\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] Any UI touched in this PR is usable by keyboard only (learn more\r\nabout [keyboard accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] This renders correctly on smaller devices using a responsive\r\nlayout. (You can test this [in your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))","sha":"0ee7a7a76d8eefc5ab44e0320a44edc178746212"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.16.0","labelRegex":"^v8.16.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/187148","number":187148,"mergeCommit":{"message":"[Security Solutions] Add a preview button to alerts inside the risk contribution panel (#187148)\n\n## Summary\r\n\r\nThe feature is hidden behind the flag `entityAlertPreviewEnabled`\r\n\r\n* It adds the extra column to the risk contribution panel with a button\r\nthat opens the alert preview panel\r\n\r\n\r\n\r\n0677de6b
-a6fa-461b-92b5-188d79e7274c\r\n\r\n\r\n\r\n\r\n### Checklist\r\n\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] Any UI touched in this PR is usable by keyboard only (learn more\r\nabout [keyboard accessibility](https://webaim.org/techniques/keyboard/))\r\n- [x] This renders correctly on smaller devices using a responsive\r\nlayout. (You can test this [in your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))","sha":"0ee7a7a76d8eefc5ab44e0320a44edc178746212"}}]}] BACKPORT-->
This commit is contained in:
parent
779668816d
commit
02533fa7e7
16 changed files with 284 additions and 86 deletions
|
@ -14,7 +14,7 @@ import { RiskInputsTab } from './tabs/risk_inputs/risk_inputs_tab';
|
|||
|
||||
export const RISK_INPUTS_TAB_TEST_ID = `${PREFIX}RiskInputsTab` as const;
|
||||
|
||||
export const getRiskInputTab = ({ entityType, entityName }: RiskInputsTabProps) => ({
|
||||
export const getRiskInputTab = ({ entityType, entityName, scopeId }: RiskInputsTabProps) => ({
|
||||
id: EntityDetailsLeftPanelTab.RISK_INPUTS,
|
||||
'data-test-subj': RISK_INPUTS_TAB_TEST_ID,
|
||||
name: (
|
||||
|
@ -23,5 +23,5 @@ export const getRiskInputTab = ({ entityType, entityName }: RiskInputsTabProps)
|
|||
defaultMessage="Risk contributions"
|
||||
/>
|
||||
),
|
||||
content: <RiskInputsTab entityType={entityType} entityName={entityName} />,
|
||||
content: <RiskInputsTab entityType={entityType} entityName={entityName} scopeId={scopeId} />,
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import { render } from '@testing-library/react';
|
|||
import React from 'react';
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { times } from 'lodash/fp';
|
||||
import { RiskInputsTab } from './risk_inputs_tab';
|
||||
import { EXPAND_ALERT_TEST_ID, RiskInputsTab } from './risk_inputs_tab';
|
||||
import { alertInputDataMock } from '../../mocks';
|
||||
import { RiskSeverity } from '../../../../../../common/search_strategy';
|
||||
import { RiskScoreEntity } from '../../../../../../common/entity_analytics/risk_engine';
|
||||
|
@ -49,6 +49,12 @@ const riskScore = {
|
|||
},
|
||||
};
|
||||
|
||||
const mockUseIsExperimentalFeatureEnabled = jest.fn().mockReturnValue(false);
|
||||
|
||||
jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
|
||||
useIsExperimentalFeatureEnabled: () => mockUseIsExperimentalFeatureEnabled(),
|
||||
}));
|
||||
|
||||
const riskScoreWithAssetCriticalityContribution = (contribution: number) => {
|
||||
const score = JSON.parse(JSON.stringify(riskScore));
|
||||
score.user.risk.category_2_score = contribution;
|
||||
|
@ -74,7 +80,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -87,7 +93,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -116,13 +122,57 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId('risk-input-contexts-title')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('it renders alert preview button when feature flag is enable', () => {
|
||||
mockUseIsExperimentalFeatureEnabled.mockReturnValue(true);
|
||||
mockUseRiskScore.mockReturnValue({
|
||||
loading: false,
|
||||
error: false,
|
||||
data: [riskScore],
|
||||
});
|
||||
mockUseRiskContributingAlerts.mockReturnValue({
|
||||
loading: false,
|
||||
error: false,
|
||||
data: [alertInputDataMock],
|
||||
});
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getByTestId(EXPAND_ALERT_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('it does not render alert preview button when feature flag is disable', () => {
|
||||
mockUseIsExperimentalFeatureEnabled.mockReturnValue(false);
|
||||
mockUseRiskScore.mockReturnValue({
|
||||
loading: false,
|
||||
error: false,
|
||||
data: [riskScore],
|
||||
});
|
||||
mockUseRiskContributingAlerts.mockReturnValue({
|
||||
loading: false,
|
||||
error: false,
|
||||
data: [alertInputDataMock],
|
||||
});
|
||||
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId(EXPAND_ALERT_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Displays 0.00 for the asset criticality contribution if the contribution value is less than -0.01', () => {
|
||||
mockUseUiSetting.mockReturnValue([true]);
|
||||
|
||||
|
@ -134,7 +184,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
const contextsTable = getByTestId('risk-input-contexts-table');
|
||||
|
@ -153,7 +203,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
const contextsTable = getByTestId('risk-input-contexts-table');
|
||||
|
@ -172,7 +222,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -201,7 +251,7 @@ describe('RiskInputsTab', () => {
|
|||
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" />
|
||||
<RiskInputsTab entityType={RiskScoreEntity.user} entityName="elastic" scopeId={'scopeId'} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
|
|||
import { ALERT_RULE_NAME } from '@kbn/rule-data-utils';
|
||||
|
||||
import { get } from 'lodash/fp';
|
||||
import { AlertPreviewButton } from '../../../../../flyout/shared/components/alert_preview_button';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { useGlobalTime } from '../../../../../common/containers/use_global_time';
|
||||
import { useQueryInspector } from '../../../../../common/components/page/manage_query';
|
||||
import { formatRiskScore } from '../../../../common';
|
||||
|
@ -40,6 +42,7 @@ import { ActionColumn } from '../../components/action_column';
|
|||
export interface RiskInputsTabProps extends Record<string, unknown> {
|
||||
entityType: RiskScoreEntity;
|
||||
entityName: string;
|
||||
scopeId: string;
|
||||
}
|
||||
|
||||
const FIRST_RECORD_PAGINATION = {
|
||||
|
@ -47,9 +50,10 @@ const FIRST_RECORD_PAGINATION = {
|
|||
querySize: 1,
|
||||
};
|
||||
|
||||
export const EXPAND_ALERT_TEST_ID = 'risk-input-alert-preview-button';
|
||||
export const RISK_INPUTS_TAB_QUERY_ID = 'RiskInputsTabQuery';
|
||||
|
||||
export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) => {
|
||||
export const RiskInputsTab = ({ entityType, entityName, scopeId }: RiskInputsTabProps) => {
|
||||
const { setQuery, deleteQuery } = useGlobalTime();
|
||||
const [selectedItems, setSelectedItems] = useState<InputAlert[]>([]);
|
||||
|
||||
|
@ -96,9 +100,26 @@ export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) =>
|
|||
}),
|
||||
[]
|
||||
);
|
||||
const isPreviewEnabled = useIsExperimentalFeatureEnabled('entityAlertPreviewEnabled');
|
||||
|
||||
const inputColumns: Array<EuiBasicTableColumn<InputAlert>> = useMemo(
|
||||
() => [
|
||||
...(isPreviewEnabled
|
||||
? [
|
||||
{
|
||||
render: (data: InputAlert) => (
|
||||
<AlertPreviewButton
|
||||
id={data._id}
|
||||
indexName={data.input.index}
|
||||
scopeId={scopeId}
|
||||
data-test-subj={EXPAND_ALERT_TEST_ID}
|
||||
/>
|
||||
),
|
||||
width: '5%',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
name: (
|
||||
<FormattedMessage
|
||||
|
@ -153,7 +174,7 @@ export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) =>
|
|||
render: formatContribution,
|
||||
},
|
||||
],
|
||||
[]
|
||||
[isPreviewEnabled, scopeId]
|
||||
);
|
||||
|
||||
const [isAssetCriticalityEnabled] = useUiSetting$<boolean>(ENABLE_ASSET_CRITICALITY_SETTING);
|
||||
|
|
|
@ -16,7 +16,7 @@ import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_ex
|
|||
import { mockFlyoutApi } from '../../shared/mocks/mock_flyout_context';
|
||||
import { mockContextValue } from '../../shared/mocks/mock_context';
|
||||
import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys';
|
||||
import { ALERT_PREVIEW_BANNER } from '../../preview';
|
||||
import { ALERT_PREVIEW_BANNER } from '../../preview/constants';
|
||||
import { DocumentDetailsContext } from '../../shared/context';
|
||||
|
||||
jest.mock('../hooks/use_paginated_alerts');
|
||||
|
|
|
@ -7,16 +7,14 @@
|
|||
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import React, { type FC, useMemo, useCallback } from 'react';
|
||||
import { type Criteria, EuiBasicTable, formatDate, EuiButtonIcon } from '@elastic/eui';
|
||||
import { type Criteria, EuiBasicTable, formatDate } from '@elastic/eui';
|
||||
import { Severity } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { ALERT_REASON, ALERT_RULE_NAME } from '@kbn/rule-data-utils';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useDocumentDetailsContext } from '../../shared/context';
|
||||
import { CORRELATIONS_DETAILS_ALERT_PREVIEW_BUTTON_TEST_ID } from './test_ids';
|
||||
import { CellTooltipWrapper } from '../../shared/components/cell_tooltip_wrapper';
|
||||
import type { DataProvider } from '../../../../../common/types';
|
||||
|
@ -26,51 +24,11 @@ import { ExpandablePanel } from '../../../shared/components/expandable_panel';
|
|||
import { InvestigateInTimelineButton } from '../../../../common/components/event_details/table/investigate_in_timeline_button';
|
||||
import { ACTION_INVESTIGATE_IN_TIMELINE } from '../../../../detections/components/alerts_table/translations';
|
||||
import { getDataProvider } from '../../../../common/components/event_details/table/use_action_cell_data_provider';
|
||||
import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys';
|
||||
import { ALERT_PREVIEW_BANNER } from '../../preview';
|
||||
import { AlertPreviewButton } from '../../../shared/components/alert_preview_button';
|
||||
|
||||
export const TIMESTAMP_DATE_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS';
|
||||
const dataProviderLimit = 5;
|
||||
|
||||
interface AlertPreviewButtonProps {
|
||||
/**
|
||||
* Id of the document
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Name of the index used in the parent's page
|
||||
*/
|
||||
indexName: string;
|
||||
}
|
||||
|
||||
const AlertPreviewButton: FC<AlertPreviewButtonProps> = ({ id, indexName }) => {
|
||||
const { openPreviewPanel } = useExpandableFlyoutApi();
|
||||
const { scopeId } = useDocumentDetailsContext();
|
||||
|
||||
const openAlertPreview = useCallback(
|
||||
() =>
|
||||
openPreviewPanel({
|
||||
id: DocumentDetailsPreviewPanelKey,
|
||||
params: {
|
||||
id,
|
||||
indexName,
|
||||
scopeId,
|
||||
isPreviewMode: true,
|
||||
banner: ALERT_PREVIEW_BANNER,
|
||||
},
|
||||
}),
|
||||
[openPreviewPanel, id, indexName, scopeId]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
iconType="expand"
|
||||
data-test-subj={CORRELATIONS_DETAILS_ALERT_PREVIEW_BUTTON_TEST_ID}
|
||||
onClick={openAlertPreview}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export interface CorrelationsDetailsAlertsTableProps {
|
||||
/**
|
||||
* Text to display in the ExpandablePanel title section
|
||||
|
@ -172,7 +130,12 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr
|
|||
? [
|
||||
{
|
||||
render: (row: Record<string, unknown>) => (
|
||||
<AlertPreviewButton id={row.id as string} indexName={row.index as string} />
|
||||
<AlertPreviewButton
|
||||
id={row.id as string}
|
||||
indexName={row.index as string}
|
||||
data-test-subj={CORRELATIONS_DETAILS_ALERT_PREVIEW_BUTTON_TEST_ID}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
),
|
||||
width: '5%',
|
||||
},
|
||||
|
@ -247,7 +210,7 @@ export const CorrelationsDetailsAlertsTable: FC<CorrelationsDetailsAlertsTablePr
|
|||
},
|
||||
},
|
||||
],
|
||||
[isPreviewEnabled]
|
||||
[isPreviewEnabled, scopeId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
export const ALERT_PREVIEW_BANNER = {
|
||||
title: i18n.translate(
|
||||
'xpack.securitySolution.flyout.left.insights.correlations.alertPreviewTitle',
|
||||
{
|
||||
defaultMessage: 'Preview alert details',
|
||||
}
|
||||
),
|
||||
backgroundColor: 'warning',
|
||||
textColor: 'warning',
|
||||
};
|
|
@ -8,7 +8,6 @@
|
|||
import type { FC } from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DocumentDetailsPreviewPanelKey } from '../shared/constants/panel_keys';
|
||||
import { useTabs } from '../right/hooks/use_tabs';
|
||||
import { useFlyoutIsExpandable } from '../right/hooks/use_flyout_is_expandable';
|
||||
|
@ -18,17 +17,7 @@ import { PanelHeader } from '../right/header';
|
|||
import { PanelContent } from '../right/content';
|
||||
import { PreviewPanelFooter } from './footer';
|
||||
import type { RightPanelTabType } from '../right/tabs';
|
||||
|
||||
export const ALERT_PREVIEW_BANNER = {
|
||||
title: i18n.translate(
|
||||
'xpack.securitySolution.flyout.left.insights.correlations.alertPreviewTitle',
|
||||
{
|
||||
defaultMessage: 'Preview alert details',
|
||||
}
|
||||
),
|
||||
backgroundColor: 'warning',
|
||||
textColor: 'warning',
|
||||
};
|
||||
import { ALERT_PREVIEW_BANNER } from './constants';
|
||||
|
||||
/**
|
||||
* Panel to be displayed in the document details expandable flyout on top of right section
|
||||
|
|
|
@ -41,16 +41,22 @@ jest.mock('../../../entity_analytics/api/hooks/use_risk_score', () => ({
|
|||
|
||||
describe('HostDetailsPanel', () => {
|
||||
it('render risk inputs panel', () => {
|
||||
const { getByTestId } = render(<HostDetailsPanel name="elastic" isRiskScoreExist={true} />, {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
const { getByTestId } = render(
|
||||
<HostDetailsPanel name="elastic" isRiskScoreExist={true} scopeId={'scopeId'} />,
|
||||
{
|
||||
wrapper: TestProviders,
|
||||
}
|
||||
);
|
||||
expect(getByTestId(RISK_INPUTS_TAB_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render risk inputs panel when no alerts ids are provided", () => {
|
||||
const { queryByTestId } = render(<HostDetailsPanel name="elastic" isRiskScoreExist={false} />, {
|
||||
wrapper: TestProviders,
|
||||
});
|
||||
const { queryByTestId } = render(
|
||||
<HostDetailsPanel name="elastic" isRiskScoreExist={false} scopeId={'scopeId'} />,
|
||||
{
|
||||
wrapper: TestProviders,
|
||||
}
|
||||
);
|
||||
expect(queryByTestId(RISK_INPUTS_TAB_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine
|
|||
export interface HostDetailsPanelProps extends Record<string, unknown> {
|
||||
isRiskScoreExist: boolean;
|
||||
name: string;
|
||||
scopeId: string;
|
||||
}
|
||||
export interface HostDetailsExpandableFlyoutProps extends FlyoutPanelProps {
|
||||
key: 'host_details';
|
||||
|
@ -25,18 +26,18 @@ export interface HostDetailsExpandableFlyoutProps extends FlyoutPanelProps {
|
|||
}
|
||||
export const HostDetailsPanelKey: HostDetailsExpandableFlyoutProps['key'] = 'host_details';
|
||||
|
||||
export const HostDetailsPanel = ({ name, isRiskScoreExist }: HostDetailsPanelProps) => {
|
||||
export const HostDetailsPanel = ({ name, isRiskScoreExist, scopeId }: HostDetailsPanelProps) => {
|
||||
// Temporary implementation while Host details left panel don't have Asset tabs
|
||||
const [tabs, selectedTabId, setSelectedTabId] = useMemo(() => {
|
||||
const isRiskScoreTabAvailable = isRiskScoreExist && name;
|
||||
return [
|
||||
isRiskScoreTabAvailable
|
||||
? [getRiskInputTab({ entityName: name, entityType: RiskScoreEntity.host })]
|
||||
? [getRiskInputTab({ entityName: name, entityType: RiskScoreEntity.host, scopeId })]
|
||||
: [],
|
||||
EntityDetailsLeftPanelTab.RISK_INPUTS,
|
||||
() => {},
|
||||
];
|
||||
}, [name, isRiskScoreExist]);
|
||||
}, [name, isRiskScoreExist, scopeId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -112,12 +112,13 @@ export const HostPanel = ({
|
|||
id: HostDetailsPanelKey,
|
||||
params: {
|
||||
name: hostName,
|
||||
scopeId,
|
||||
isRiskScoreExist,
|
||||
path: tab ? { tab } : undefined,
|
||||
},
|
||||
});
|
||||
},
|
||||
[telemetry, openLeftPanel, hostName, isRiskScoreExist]
|
||||
[telemetry, openLeftPanel, hostName, isRiskScoreExist, scopeId]
|
||||
);
|
||||
|
||||
const openDefaultPanel = useCallback(() => openTabPanel(), [openTabPanel]);
|
||||
|
|
|
@ -20,6 +20,7 @@ describe('LeftPanel', () => {
|
|||
}}
|
||||
isRiskScoreExist
|
||||
user={{ name: 'test user', email: [] }}
|
||||
scopeId={'scopeId'}
|
||||
/>,
|
||||
{
|
||||
wrapper: TestProviders,
|
||||
|
@ -39,6 +40,7 @@ describe('LeftPanel', () => {
|
|||
}}
|
||||
isRiskScoreExist={false}
|
||||
user={{ name: 'test user', email: [] }}
|
||||
scopeId={'scopeId'}
|
||||
/>,
|
||||
{
|
||||
wrapper: TestProviders,
|
||||
|
|
|
@ -27,6 +27,7 @@ export interface UserDetailsPanelProps extends Record<string, unknown> {
|
|||
isRiskScoreExist: boolean;
|
||||
user: UserParam;
|
||||
path?: PanelPath;
|
||||
scopeId: string;
|
||||
}
|
||||
export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps {
|
||||
key: 'user_details';
|
||||
|
@ -34,9 +35,14 @@ export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps {
|
|||
}
|
||||
export const UserDetailsPanelKey: UserDetailsExpandableFlyoutProps['key'] = 'user_details';
|
||||
|
||||
export const UserDetailsPanel = ({ isRiskScoreExist, user, path }: UserDetailsPanelProps) => {
|
||||
export const UserDetailsPanel = ({
|
||||
isRiskScoreExist,
|
||||
user,
|
||||
path,
|
||||
scopeId,
|
||||
}: UserDetailsPanelProps) => {
|
||||
const managedUser = useManagedUser(user.name, user.email);
|
||||
const tabs = useTabs(managedUser.data, user.name, isRiskScoreExist);
|
||||
const tabs = useTabs(managedUser.data, user.name, isRiskScoreExist, scopeId);
|
||||
const { selectedTabId, setSelectedTabId } = useSelectedTab(isRiskScoreExist, user, tabs, path);
|
||||
|
||||
if (managedUser.isLoading) return <FlyoutLoading />;
|
||||
|
|
|
@ -25,7 +25,8 @@ import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_
|
|||
export const useTabs = (
|
||||
managedUser: ManagedUserHits,
|
||||
name: string,
|
||||
isRiskScoreExist: boolean
|
||||
isRiskScoreExist: boolean,
|
||||
scopeId: string
|
||||
): LeftPanelTabsType =>
|
||||
useMemo(() => {
|
||||
const tabs: LeftPanelTabsType = [];
|
||||
|
@ -37,6 +38,7 @@ export const useTabs = (
|
|||
getRiskInputTab({
|
||||
entityName: name,
|
||||
entityType: RiskScoreEntity.user,
|
||||
scopeId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -50,7 +52,7 @@ export const useTabs = (
|
|||
}
|
||||
|
||||
return tabs;
|
||||
}, [isRiskScoreExist, managedUser, name]);
|
||||
}, [isRiskScoreExist, managedUser, name, scopeId]);
|
||||
|
||||
const getOktaTab = (oktaManagedUser: ManagedUserHit) => ({
|
||||
id: EntityDetailsLeftPanelTab.OKTA,
|
||||
|
|
|
@ -115,6 +115,7 @@ export const UserPanel = ({
|
|||
id: UserDetailsPanelKey,
|
||||
params: {
|
||||
isRiskScoreExist: !!userRiskData?.user?.risk,
|
||||
scopeId,
|
||||
user: {
|
||||
name: userName,
|
||||
email,
|
||||
|
@ -123,7 +124,7 @@ export const UserPanel = ({
|
|||
path: tab ? { tab } : undefined,
|
||||
});
|
||||
},
|
||||
[telemetry, email, openLeftPanel, userName, userRiskData]
|
||||
[telemetry, openLeftPanel, userRiskData?.user?.risk, userName, email, scopeId]
|
||||
);
|
||||
|
||||
const openPanelFirstTab = useCallback(() => openPanelTab(), [openPanelTab]);
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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 { fireEvent, render } from '@testing-library/react';
|
||||
import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout';
|
||||
import React from 'react';
|
||||
import { AlertPreviewButton } from './alert_preview_button';
|
||||
import { DocumentDetailsPreviewPanelKey } from '../../document_details/shared/constants/panel_keys';
|
||||
import { ALERT_PREVIEW_BANNER } from '../../document_details/preview/constants';
|
||||
|
||||
const mockOpenPreviewPanel = jest.fn();
|
||||
jest.mock('@kbn/expandable-flyout', () => {
|
||||
return {
|
||||
useExpandableFlyoutApi: () => ({
|
||||
openPreviewPanel: mockOpenPreviewPanel,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('AlertPreviewButton', () => {
|
||||
it('renders the icon', () => {
|
||||
const { getByTestId } = render(
|
||||
<AlertPreviewButton
|
||||
id="1"
|
||||
indexName="index"
|
||||
scopeId="scope"
|
||||
data-test-subj="alertPreviewButton"
|
||||
/>,
|
||||
{ wrapper: ExpandableFlyoutProvider }
|
||||
);
|
||||
expect(getByTestId('alertPreviewButton')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('opens the preview panel when clicked', () => {
|
||||
const id = '1';
|
||||
const indexName = 'index';
|
||||
const scopeId = 'scope';
|
||||
|
||||
const { getByTestId } = render(
|
||||
<AlertPreviewButton
|
||||
id={id}
|
||||
indexName={indexName}
|
||||
scopeId={scopeId}
|
||||
data-test-subj="alertPreviewButton"
|
||||
/>,
|
||||
{ wrapper: ExpandableFlyoutProvider }
|
||||
);
|
||||
fireEvent.click(getByTestId('alertPreviewButton'));
|
||||
|
||||
expect(mockOpenPreviewPanel).toHaveBeenCalledWith({
|
||||
id: DocumentDetailsPreviewPanelKey,
|
||||
params: {
|
||||
id,
|
||||
indexName,
|
||||
scopeId,
|
||||
isPreviewMode: true,
|
||||
banner: ALERT_PREVIEW_BANNER,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 } from '@elastic/eui';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import React, { useCallback } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_PREVIEW_BANNER } from '../../document_details/preview/constants';
|
||||
import { DocumentDetailsPreviewPanelKey } from '../../document_details/shared/constants/panel_keys';
|
||||
|
||||
interface AlertPreviewButtonProps {
|
||||
/**
|
||||
* Name of the index used in the parent's page
|
||||
*/
|
||||
indexName: string;
|
||||
/**
|
||||
* Id of the alert to preview
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Data attribute used for testing.
|
||||
*/
|
||||
'data-test-subj'?: string;
|
||||
/**
|
||||
* Maintain backwards compatibility // TODO remove when possible
|
||||
*/
|
||||
scopeId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon button showed on tables to launch a preview of the alert details panel.
|
||||
*/
|
||||
export const AlertPreviewButton: FC<AlertPreviewButtonProps> = ({
|
||||
id,
|
||||
indexName,
|
||||
'data-test-subj': dataTestSubj,
|
||||
scopeId,
|
||||
}) => {
|
||||
const { openPreviewPanel } = useExpandableFlyoutApi();
|
||||
|
||||
const openAlertPreview = useCallback(
|
||||
() =>
|
||||
openPreviewPanel({
|
||||
id: DocumentDetailsPreviewPanelKey,
|
||||
params: {
|
||||
id,
|
||||
indexName,
|
||||
scopeId,
|
||||
isPreviewMode: true,
|
||||
banner: ALERT_PREVIEW_BANNER,
|
||||
},
|
||||
}),
|
||||
[openPreviewPanel, id, indexName, scopeId]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
iconType="expand"
|
||||
data-test-subj={dataTestSubj}
|
||||
onClick={openAlertPreview}
|
||||
aria-label={i18n.translate('xpack.securitySolution.flyout.right.alertPreview.ariaLabel', {
|
||||
defaultMessage: 'Preview alert with id {id}',
|
||||
values: { id },
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue