Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] expandable flyout - add no data message in entities details and entities overview components #164955

Merged
merged 2 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import '@testing-library/jest-dom';
import { LeftPanelContext } from '../context';
import { TestProviders } from '../../../common/mock';
import { EntitiesDetails } from './entities_details';
import { ENTITIES_DETAILS_TEST_ID, HOST_DETAILS_TEST_ID, USER_DETAILS_TEST_ID } from './test_ids';
import {
ENTITIES_DETAILS_NO_DATA_TEST_ID,
ENTITIES_DETAILS_TEST_ID,
HOST_DETAILS_TEST_ID,
USER_DETAILS_TEST_ID,
} from './test_ids';
import { mockContextValue } from '../mocks/mock_context';
import { EXPANDABLE_PANEL_CONTENT_TEST_ID } from '../../shared/components/test_ids';

Expand Down Expand Up @@ -49,8 +54,8 @@ describe('<EntitiesDetails />', () => {
expect(getByTestId(HOST_TEST_ID)).toBeInTheDocument();
});

it('does not render user and host details if user name and host name are not available', () => {
const { queryByTestId } = render(
it('should render no data message if user name and host name are not available', () => {
const { getByTestId, queryByTestId } = render(
<TestProviders>
<LeftPanelContext.Provider
value={{
Expand All @@ -63,12 +68,13 @@ describe('<EntitiesDetails />', () => {
</LeftPanelContext.Provider>
</TestProviders>
);
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument();
});

it('does not render user and host details if @timestamp is not available', () => {
const { queryByTestId } = render(
const { getByTestId, queryByTestId } = render(
<TestProviders>
<LeftPanelContext.Provider
value={{
Expand All @@ -89,6 +95,7 @@ describe('<EntitiesDetails />', () => {
</LeftPanelContext.Provider>
</TestProviders>
);
expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument();
expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { ENTITIES_NO_DATA_MESSAGE } from './translations';
import { useLeftPanelContext } from '../context';
import { getField } from '../../shared/utils';
import { UserDetails } from './user_details';
import { HostDetails } from './host_details';
import { ENTITIES_DETAILS_TEST_ID } from './test_ids';
import { ENTITIES_DETAILS_NO_DATA_TEST_ID, ENTITIES_DETAILS_TEST_ID } from './test_ids';

export const ENTITIES_TAB_ID = 'entities-details';

Expand All @@ -24,19 +25,29 @@ export const EntitiesDetails: React.FC = () => {
const userName = getField(getFieldsData('user.name'));
const timestamp = getField(getFieldsData('@timestamp'));

const showDetails = timestamp && (hostName || userName);
const showUserDetails = userName && timestamp;
const showHostDetails = hostName && timestamp;

return (
<EuiFlexGroup direction="column" gutterSize="m" data-test-subj={ENTITIES_DETAILS_TEST_ID}>
{userName && timestamp && (
<EuiFlexItem>
<UserDetails userName={userName} timestamp={timestamp} scopeId={scopeId} />
</EuiFlexItem>
)}
{hostName && timestamp && (
<EuiFlexItem>
<HostDetails hostName={hostName} timestamp={timestamp} scopeId={scopeId} />
</EuiFlexItem>
<>
{showDetails ? (
<EuiFlexGroup direction="column" gutterSize="m" data-test-subj={ENTITIES_DETAILS_TEST_ID}>
{showUserDetails && (
<EuiFlexItem>
<UserDetails userName={userName} timestamp={timestamp} scopeId={scopeId} />
</EuiFlexItem>
)}
{showHostDetails && (
<EuiFlexItem>
<HostDetails hostName={hostName} timestamp={timestamp} scopeId={scopeId} />
</EuiFlexItem>
)}
</EuiFlexGroup>
) : (
<div data-test-subj={ENTITIES_DETAILS_NO_DATA_TEST_ID}>{ENTITIES_NO_DATA_MESSAGE}</div>
)}
</EuiFlexGroup>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const PREVALENCE_DETAILS_TABLE_NO_DATA_TEST_ID =
/* Entities */

export const ENTITIES_DETAILS_TEST_ID = `${PREFIX}EntitiesDetails` as const;
export const ENTITIES_DETAILS_NO_DATA_TEST_ID = `${ENTITIES_DETAILS_TEST_ID}NoData` as const;
export const USER_DETAILS_TEST_ID = `${PREFIX}UsersDetails` as const;
export const USER_DETAILS_INFO_TEST_ID = 'user-overview';
export const USER_DETAILS_RELATED_HOSTS_TABLE_TEST_ID =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

import { i18n } from '@kbn/i18n';

export const ENTITIES_NO_DATA_MESSAGE = i18n.translate(
'xpack.securitySolution.flyout.entitiesNoDataMessage',
{
defaultMessage: 'No user or host data available',
}
);

export const ANALYZER_ERROR_MESSAGE = i18n.translate(
'xpack.securitySolution.flyout.analyzerErrorMessage',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { RightPanelContext } from '../context';
import {
ENTITIES_HOST_OVERVIEW_TEST_ID,
ENTITIES_USER_OVERVIEW_TEST_ID,
INSIGHTS_ENTITIES_NO_DATA_TEST_ID,
INSIGHTS_ENTITIES_TEST_ID,
} from './test_ids';
import { EntitiesOverview } from './entities_overview';
Expand All @@ -28,16 +29,18 @@ const TITLE_LINK_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_LINK_TEST_ID(INSIGHTS_E
const TITLE_ICON_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_ICON_TEST_ID(INSIGHTS_ENTITIES_TEST_ID);
const TITLE_TEXT_TEST_ID = EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID(INSIGHTS_ENTITIES_TEST_ID);

const mockContextValue = {
eventId: 'event id',
indexName: 'index',
scopeId: 'scopeId',
getFieldsData: mockGetFieldsData,
} as unknown as RightPanelContext;

describe('<EntitiesOverview />', () => {
it('should render wrapper component', () => {
const contextValue = {
eventId: 'event id',
getFieldsData: mockGetFieldsData,
} as unknown as RightPanelContext;

const { getByTestId, queryByTestId } = render(
<TestProviders>
<RightPanelContext.Provider value={contextValue}>
<RightPanelContext.Provider value={mockContextValue}>
<EntitiesOverview />
</RightPanelContext.Provider>
</TestProviders>
Expand All @@ -51,14 +54,9 @@ describe('<EntitiesOverview />', () => {
});

it('should render user and host', () => {
const contextValue = {
eventId: 'event id',
getFieldsData: mockGetFieldsData,
} as unknown as RightPanelContext;

const { getByTestId } = render(
<TestProviders>
<RightPanelContext.Provider value={contextValue}>
<RightPanelContext.Provider value={mockContextValue}>
<EntitiesOverview />
</RightPanelContext.Provider>
</TestProviders>
Expand All @@ -69,7 +67,7 @@ describe('<EntitiesOverview />', () => {

it('should only render user when host name is null', () => {
const contextValue = {
eventId: 'event id',
...mockContextValue,
getFieldsData: (field: string) => (field === 'user.name' ? 'user1' : null),
} as unknown as RightPanelContext;

Expand All @@ -87,7 +85,7 @@ describe('<EntitiesOverview />', () => {

it('should only render host when user name is null', () => {
const contextValue = {
eventId: 'event id',
...mockContextValue,
getFieldsData: (field: string) => (field === 'host.name' ? 'host1' : null),
} as unknown as RightPanelContext;

Expand All @@ -103,27 +101,61 @@ describe('<EntitiesOverview />', () => {
expect(queryByTestId(ENTITIES_USER_OVERVIEW_TEST_ID)).not.toBeInTheDocument();
});

it('should not render if both host name and user name are null/blank', () => {
it('should render no data message if both host name and user name are null/blank', () => {
const contextValue = {
eventId: 'event id',
...mockContextValue,
getFieldsData: (field: string) => {},
} as unknown as RightPanelContext;

const { container } = render(
const { queryByTestId } = render(
<TestProviders>
<RightPanelContext.Provider value={contextValue}>
<EntitiesOverview />
</RightPanelContext.Provider>
</TestProviders>
);

expect(container).toBeEmptyDOMElement();
expect(queryByTestId(INSIGHTS_ENTITIES_NO_DATA_TEST_ID)).toBeInTheDocument();
});

it('should not render if eventId is null', () => {
const contextValue = {
...mockContextValue,
eventId: null,
getFieldsData: (field: string) => {},
} as unknown as RightPanelContext;

const { container } = render(
<TestProviders>
<RightPanelContext.Provider value={contextValue}>
<EntitiesOverview />
</RightPanelContext.Provider>
</TestProviders>
);

expect(container).toBeEmptyDOMElement();
});

it('should not render if indexName is null', () => {
const contextValue = {
...mockContextValue,
indexName: null,
} as unknown as RightPanelContext;

const { container } = render(
<TestProviders>
<RightPanelContext.Provider value={contextValue}>
<EntitiesOverview />
</RightPanelContext.Provider>
</TestProviders>
);

expect(container).toBeEmptyDOMElement();
});

it('should not render if scopeId is null', () => {
const contextValue = {
...mockContextValue,
scopeId: null,
} as unknown as RightPanelContext;

const { container } = render(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
import React, { useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { INSIGHTS_ENTITIES_NO_DATA_TEST_ID, INSIGHTS_ENTITIES_TEST_ID } from './test_ids';
import { ExpandablePanel } from '../../shared/components/expandable_panel';
import { useRightPanelContext } from '../context';
import { INSIGHTS_ENTITIES_TEST_ID } from './test_ids';
import { ENTITIES_TITLE } from './translations';
import { ENTITIES_NO_DATA_MESSAGE, ENTITIES_TITLE } from './translations';
import { getField } from '../../shared/utils';
import { HostEntityOverview } from './host_entity_overview';
import { UserEntityOverview } from './user_entity_overview';
Expand Down Expand Up @@ -42,7 +42,7 @@ export const EntitiesOverview: React.FC = () => {
});
}, [eventId, openLeftPanel, indexName, scopeId]);

if (!eventId || (!userName && !hostName)) {
if (!eventId || !indexName || !scopeId) {
return null;
}

Expand All @@ -56,19 +56,23 @@ export const EntitiesOverview: React.FC = () => {
}}
data-test-subj={INSIGHTS_ENTITIES_TEST_ID}
>
<EuiFlexGroup direction="column" gutterSize="s">
{userName && (
<EuiFlexItem>
<UserEntityOverview userName={userName} />
</EuiFlexItem>
)}
<EuiSpacer size="s" />
{hostName && (
<EuiFlexItem>
<HostEntityOverview hostName={hostName} />
</EuiFlexItem>
)}
</EuiFlexGroup>
{userName || hostName ? (
<EuiFlexGroup direction="column" gutterSize="s">
{userName && (
<EuiFlexItem>
<UserEntityOverview userName={userName} />
</EuiFlexItem>
)}
<EuiSpacer size="s" />
{hostName && (
<EuiFlexItem>
<HostEntityOverview hostName={hostName} />
</EuiFlexItem>
)}
</EuiFlexGroup>
) : (
<div data-test-subj={INSIGHTS_ENTITIES_NO_DATA_TEST_ID}>{ENTITIES_NO_DATA_MESSAGE}</div>
)}
</ExpandablePanel>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const SUMMARY_ROW_VALUE_TEST_ID = (dataTestSubj: string) => `${dataTestSu
/* Insights Entities */

export const INSIGHTS_ENTITIES_TEST_ID = 'securitySolutionDocumentDetailsFlyoutInsightsEntities';
export const INSIGHTS_ENTITIES_NO_DATA_TEST_ID = `${INSIGHTS_ENTITIES_TEST_ID}NoData` as const;
export const ENTITIES_USER_OVERVIEW_TEST_ID =
'securitySolutionDocumentDetailsFlyoutEntitiesUserOverview';
export const ENTITIES_USER_OVERVIEW_LINK_TEST_ID = `${ENTITIES_USER_OVERVIEW_TEST_ID}Link`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ export const ENTITIES_TITLE = i18n.translate(
{ defaultMessage: 'Entities' }
);

export const ENTITIES_NO_DATA_MESSAGE = i18n.translate(
'xpack.securitySolution.flyout.documentDetails.entitiesNoDataMessage',
{
defaultMessage: 'No user or host data available',
}
);

export const THREAT_INTELLIGENCE_TITLE = i18n.translate(
'xpack.securitySolution.flyout.documentDetails.threatIntelligenceTitle',
{ defaultMessage: 'Threat Intelligence' }
Expand Down