[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:
Kibana Machine 2022-08-10 10:41:55 -04:00 committed by GitHub
parent 0822e376c7
commit 470e8cf349
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 187 additions and 360 deletions

View file

@ -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>
`;

View file

@ -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',
},
});
});
});
});

View file

@ -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}
/>
)}

View file

@ -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',
},
});
});
});
});

View file

@ -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}
/>
)}