mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Host/User details flyout, fix ML narrow date range (#138514)
(cherry picked from commit 08d14ef2c0
)
Co-authored-by: Steph Milovic <stephanie.milovic@elastic.co>
This commit is contained in:
parent
0822e376c7
commit
470e8cf349
5 changed files with 187 additions and 360 deletions
|
@ -1,341 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Expandable Host Component ExpandableHostDetails: rendering it should render the HostOverview of the ExpandableHostDetails 1`] = `
|
||||
.c3 {
|
||||
color: #535966;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.c2 dt {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.c2 dd {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c2 dd > div {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.c1 .euiButtonIcon {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
width: 100%;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex-grow: 1;
|
||||
-ms-flex-positive: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.c0 > * {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.c0 .inspectButtonComponent {
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 250ms ease;
|
||||
transition: opacity 250ms ease;
|
||||
}
|
||||
|
||||
.c0:hover .inspectButtonComponent {
|
||||
pointer-events: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.c4 {
|
||||
padding: 12px;
|
||||
background: rgba(250,251,253,0.9);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
<div
|
||||
class="c0"
|
||||
data-test-subj="hoverVisibilityContainer"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionColumn euiFlexGroup--responsive c1"
|
||||
data-test-subj="host-overview"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
>
|
||||
<dl
|
||||
class="euiDescriptionList euiDescriptionList--row c2"
|
||||
>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Host ID
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
First seen
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
aria-label="Loading"
|
||||
class="euiLoadingSpinner emotion-euiLoadingSpinner-m"
|
||||
data-test-subj="loading-spinner"
|
||||
role="progressbar"
|
||||
/>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Last seen
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
aria-label="Loading"
|
||||
class="euiLoadingSpinner emotion-euiLoadingSpinner-m"
|
||||
data-test-subj="loading-spinner"
|
||||
role="progressbar"
|
||||
/>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
>
|
||||
<dl
|
||||
class="euiDescriptionList euiDescriptionList--row c2"
|
||||
>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
IP addresses
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
MAC addresses
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Platform
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
>
|
||||
<dl
|
||||
class="euiDescriptionList euiDescriptionList--row c2"
|
||||
>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Operating system
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Family
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Version
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Architecture
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div
|
||||
class="euiFlexItem"
|
||||
>
|
||||
<dl
|
||||
class="euiDescriptionList euiDescriptionList--row c2"
|
||||
>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Cloud provider
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Region
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Instance ID
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
<dt
|
||||
class="euiDescriptionList__title"
|
||||
>
|
||||
Machine type
|
||||
</dt>
|
||||
<dd
|
||||
class="euiDescriptionList__description"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
—
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<aside
|
||||
class="c4"
|
||||
>
|
||||
<div
|
||||
class="euiFlexGroup euiFlexGroup--gutterSmall euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentCenter euiFlexGroup--directionColumn euiFlexGroup--responsive c5"
|
||||
overlay="[object Object]"
|
||||
>
|
||||
<div
|
||||
class="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<span
|
||||
aria-label="Loading"
|
||||
class="euiLoadingSpinner emotion-euiLoadingSpinner-xl"
|
||||
data-test-subj="loading-spinner"
|
||||
role="progressbar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -6,13 +6,43 @@
|
|||
*/
|
||||
|
||||
import { mount } from 'enzyme';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import '../../../../common/mock/match_media';
|
||||
import { mockGlobalState, TestProviders } from '../../../../common/mock';
|
||||
import { ExpandableHostDetails } from './expandable_host';
|
||||
import { mockAnomalies } from '../../../../common/components/ml/mock';
|
||||
import type { Anomalies } from '../../../../common/components/ml/types';
|
||||
import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions';
|
||||
const mockDispatch = jest.fn();
|
||||
jest.mock('../../../../../common/machine_learning/has_ml_user_permissions');
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useDispatch: () => mockDispatch,
|
||||
};
|
||||
});
|
||||
jest.mock('../../../../common/components/ml/anomaly/anomaly_table_provider', () => ({
|
||||
AnomalyTableProvider: ({
|
||||
children,
|
||||
}: {
|
||||
children: (args: {
|
||||
anomaliesData: Anomalies;
|
||||
isLoadingAnomaliesData: boolean;
|
||||
}) => React.ReactNode;
|
||||
}) => children({ anomaliesData: mockAnomalies, isLoadingAnomaliesData: false }),
|
||||
}));
|
||||
|
||||
describe('Expandable Host Component', () => {
|
||||
beforeAll(() => {
|
||||
(hasMlUserPermissions as jest.Mock).mockReturnValue(true);
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
const mockProps = {
|
||||
contextID: 'text-context',
|
||||
hostName: 'testHostName',
|
||||
|
@ -26,7 +56,7 @@ describe('Expandable Host Component', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('ExpandableHostDetails').render()).toMatchSnapshot();
|
||||
expect(wrapper.find('[data-test-subj="host-overview"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('it should render the HostOverview of the ExpandableHostDetails with the correct indices', () => {
|
||||
|
@ -40,5 +70,28 @@ describe('Expandable Host Component', () => {
|
|||
mockGlobalState.sourcerer.sourcererScopes.default.selectedPatterns
|
||||
);
|
||||
});
|
||||
|
||||
test('it should set date range to anomaly date range', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExpandableHostDetails {...mockProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
wrapper.find('[data-test-subj="anomaly-score-popover"]').first().simulate('click');
|
||||
await waitFor(() => {
|
||||
wrapper
|
||||
.find('button[data-test-subj="anomaly-description-narrow-range-link"]')
|
||||
.first()
|
||||
.simulate('click');
|
||||
});
|
||||
expect(mockDispatch).toHaveBeenCalledWith({
|
||||
type: 'x-pack/security_solution/local/inputs/SET_ABSOLUTE_RANGE_DATE_PICKER',
|
||||
payload: {
|
||||
id: 'global',
|
||||
from: '2019-06-15T06:00:00.000Z',
|
||||
to: '2019-06-17T06:00:00.000Z',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { HostDetailsLink } from '../../../../common/components/links';
|
||||
import { useGlobalTime } from '../../../../common/containers/use_global_time';
|
||||
import { useSourcererDataView } from '../../../../common/containers/sourcerer';
|
||||
|
@ -65,6 +66,7 @@ export const ExpandableHostDetails = ({
|
|||
(i.e. extraneous endpoint data is retrieved from the backend leading to endpoint data not being returned)
|
||||
*/
|
||||
const { selectedPatterns } = useSourcererDataView();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [loading, { hostDetails: hostOverview }] = useHostDetails({
|
||||
endDate: to,
|
||||
|
@ -72,6 +74,19 @@ export const ExpandableHostDetails = ({
|
|||
indexNames: selectedPatterns,
|
||||
startDate: from,
|
||||
});
|
||||
const narrowDateRange = useCallback(
|
||||
(score, interval) => {
|
||||
const fromTo = scoreIntervalToDateTime(score, interval);
|
||||
dispatch(
|
||||
setAbsoluteRangeDatePicker({
|
||||
id: 'global',
|
||||
from: fromTo.from,
|
||||
to: fromTo.to,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
return (
|
||||
<AnomalyTableProvider
|
||||
criteriaFields={hostToCriteria(hostOverview)}
|
||||
|
@ -92,14 +107,7 @@ export const ExpandableHostDetails = ({
|
|||
loading={loading}
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
narrowDateRange={(score, interval) => {
|
||||
const fromTo = scoreIntervalToDateTime(score, interval);
|
||||
setAbsoluteRangeDatePicker({
|
||||
id: 'global',
|
||||
from: fromTo.from,
|
||||
to: fromTo.to,
|
||||
});
|
||||
}}
|
||||
narrowDateRange={narrowDateRange}
|
||||
hostName={hostName}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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 { mount } from 'enzyme';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import '../../../../common/mock/match_media';
|
||||
import { mockGlobalState, TestProviders } from '../../../../common/mock';
|
||||
import { ExpandableUserDetails } from './expandable_user';
|
||||
import { mockAnomalies } from '../../../../common/components/ml/mock';
|
||||
import type { Anomalies } from '../../../../common/components/ml/types';
|
||||
import { hasMlUserPermissions } from '../../../../../common/machine_learning/has_ml_user_permissions';
|
||||
const mockDispatch = jest.fn();
|
||||
jest.mock('../../../../../common/machine_learning/has_ml_user_permissions');
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useDispatch: () => mockDispatch,
|
||||
};
|
||||
});
|
||||
jest.mock('../../../../common/components/ml/anomaly/anomaly_table_provider', () => ({
|
||||
AnomalyTableProvider: ({
|
||||
children,
|
||||
}: {
|
||||
children: (args: {
|
||||
anomaliesData: Anomalies;
|
||||
isLoadingAnomaliesData: boolean;
|
||||
}) => React.ReactNode;
|
||||
}) => children({ anomaliesData: mockAnomalies, isLoadingAnomaliesData: false }),
|
||||
}));
|
||||
|
||||
describe('Expandable Host Component', () => {
|
||||
beforeAll(() => {
|
||||
(hasMlUserPermissions as jest.Mock).mockReturnValue(true);
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
const mockProps = {
|
||||
contextID: 'text-context',
|
||||
userName: 'testUserName',
|
||||
isDraggable: true,
|
||||
};
|
||||
|
||||
describe('ExpandableUserDetails: rendering', () => {
|
||||
test('it should render the UserOverview of the ExpandableUserDetails', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExpandableUserDetails {...mockProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="user-overview"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
test('it should render the UserOverview of the ExpandableUserDetails with the correct indices', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExpandableUserDetails {...mockProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('UserOverview').prop('indexPatterns')).toStrictEqual(
|
||||
mockGlobalState.sourcerer.sourcererScopes.default.selectedPatterns
|
||||
);
|
||||
});
|
||||
|
||||
test('it should set date range to anomaly date range', async () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<ExpandableUserDetails {...mockProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
wrapper.find('[data-test-subj="anomaly-score-popover"]').first().simulate('click');
|
||||
await waitFor(() => {
|
||||
wrapper
|
||||
.find('button[data-test-subj="anomaly-description-narrow-range-link"]')
|
||||
.first()
|
||||
.simulate('click');
|
||||
});
|
||||
expect(mockDispatch).toHaveBeenCalledWith({
|
||||
type: 'x-pack/security_solution/local/inputs/SET_ABSOLUTE_RANGE_DATE_PICKER',
|
||||
payload: {
|
||||
id: 'global',
|
||||
from: '2019-06-15T06:00:00.000Z',
|
||||
to: '2019-06-17T06:00:00.000Z',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -8,7 +8,8 @@
|
|||
import { EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { UserDetailsLink } from '../../../../common/components/links';
|
||||
import { UserOverview } from '../../../../overview/components/user_overview';
|
||||
import { useUserDetails } from '../../../../users/containers/users/details';
|
||||
|
@ -57,6 +58,7 @@ export const ExpandableUserDetails = ({
|
|||
}: ExpandableUserProps & { contextID: string; isDraggable?: boolean }) => {
|
||||
const { to, from, isInitializing } = useGlobalTime();
|
||||
const { selectedPatterns } = useSourcererDataView();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [loading, { userDetails }] = useUserDetails({
|
||||
endDate: to,
|
||||
|
@ -66,6 +68,20 @@ export const ExpandableUserDetails = ({
|
|||
skip: isInitializing,
|
||||
});
|
||||
|
||||
const narrowDateRange = useCallback(
|
||||
(score, interval) => {
|
||||
const fromTo = scoreIntervalToDateTime(score, interval);
|
||||
dispatch(
|
||||
setAbsoluteRangeDatePicker({
|
||||
id: 'global',
|
||||
from: fromTo.from,
|
||||
to: fromTo.to,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<AnomalyTableProvider
|
||||
criteriaFields={getCriteriaFromUsersType(UsersType.details, userName)}
|
||||
|
@ -86,14 +102,7 @@ export const ExpandableUserDetails = ({
|
|||
isLoadingAnomaliesData={isLoadingAnomaliesData}
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
narrowDateRange={(score, interval) => {
|
||||
const fromTo = scoreIntervalToDateTime(score, interval);
|
||||
setAbsoluteRangeDatePicker({
|
||||
id: 'global',
|
||||
from: fromTo.from,
|
||||
to: fromTo.to,
|
||||
});
|
||||
}}
|
||||
narrowDateRange={narrowDateRange}
|
||||
indexPatterns={selectedPatterns}
|
||||
/>
|
||||
)}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue