diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx index d02f84207ecde..eb56069bb7646 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.test.tsx @@ -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'; @@ -49,8 +54,8 @@ describe('', () => { 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( ', () => { ); + 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( ', () => { ); + expect(getByTestId(ENTITIES_DETAILS_NO_DATA_TEST_ID)).toBeInTheDocument(); expect(queryByTestId(USER_TEST_ID)).not.toBeInTheDocument(); expect(queryByTestId(HOST_TEST_ID)).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx index 29e748dd70eae..ff3678a06e428 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/entities_details.tsx @@ -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'; @@ -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 ( - - {userName && timestamp && ( - - - - )} - {hostName && timestamp && ( - - - + <> + {showDetails ? ( + + {showUserDetails && ( + + + + )} + {showHostDetails && ( + + + + )} + + ) : ( +
{ENTITIES_NO_DATA_MESSAGE}
)} -
+ ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 6c736770544bd..03b768607c20c 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -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 = diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts index dbd0cd21e129c..9e7cf56db7c05 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts @@ -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', { diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx index d26a93262fa31..528b839bb218c 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.test.tsx @@ -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'; @@ -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('', () => { it('should render wrapper component', () => { - const contextValue = { - eventId: 'event id', - getFieldsData: mockGetFieldsData, - } as unknown as RightPanelContext; - const { getByTestId, queryByTestId } = render( - + @@ -51,14 +54,9 @@ describe('', () => { }); it('should render user and host', () => { - const contextValue = { - eventId: 'event id', - getFieldsData: mockGetFieldsData, - } as unknown as RightPanelContext; - const { getByTestId } = render( - + @@ -69,7 +67,7 @@ describe('', () => { 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; @@ -87,7 +85,7 @@ describe('', () => { 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; @@ -103,13 +101,13 @@ describe('', () => { 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( @@ -117,13 +115,47 @@ describe('', () => { ); - 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( + + + + + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should not render if indexName is null', () => { + const contextValue = { + ...mockContextValue, + indexName: null, + } as unknown as RightPanelContext; + + const { container } = render( + + + + + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should not render if scopeId is null', () => { + const contextValue = { + ...mockContextValue, + scopeId: null, } as unknown as RightPanelContext; const { container } = render( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx index d74fd844ea4a9..efab7fa9f6d03 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/entities_overview.tsx @@ -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'; @@ -42,7 +42,7 @@ export const EntitiesOverview: React.FC = () => { }); }, [eventId, openLeftPanel, indexName, scopeId]); - if (!eventId || (!userName && !hostName)) { + if (!eventId || !indexName || !scopeId) { return null; } @@ -56,19 +56,23 @@ export const EntitiesOverview: React.FC = () => { }} data-test-subj={INSIGHTS_ENTITIES_TEST_ID} > - - {userName && ( - - - - )} - - {hostName && ( - - - - )} - + {userName || hostName ? ( + + {userName && ( + + + + )} + + {hostName && ( + + + + )} + + ) : ( +
{ENTITIES_NO_DATA_MESSAGE}
+ )} ); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts index c4eb67f5ddba9..9c810f7bfc696 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts @@ -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`; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 3a04a68ac0ebf..4d1390701ad10 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -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' }