diff --git a/x-pack/plugins/security_solution/common/ecs/agent/index.ts b/x-pack/plugins/security_solution/common/ecs/agent/index.ts new file mode 100644 index 0000000000000..6f29a2020c944 --- /dev/null +++ b/x-pack/plugins/security_solution/common/ecs/agent/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface AgentEcs { + type?: string[]; +} diff --git a/x-pack/plugins/security_solution/common/ecs/endgame/index.ts b/x-pack/plugins/security_solution/common/ecs/endgame/index.ts index f435db4f47810..d2fc5d61527a5 100644 --- a/x-pack/plugins/security_solution/common/ecs/endgame/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/endgame/index.ts @@ -5,29 +5,17 @@ */ export interface EndgameEcs { - exit_code?: number; - - file_name?: string; - - file_path?: string; - - logon_type?: number; - - parent_process_name?: string; - - pid?: number; - - process_name?: string; - - subject_domain_name?: string; - - subject_logon_id?: string; - - subject_user_name?: string; - - target_domain_name?: string; - - target_logon_id?: string; - - target_user_name?: string; + exit_code?: number[]; + file_name?: string[]; + file_path?: string[]; + logon_type?: number[]; + parent_process_name?: string[]; + pid?: number[]; + process_name?: string[]; + subject_domain_name?: string[]; + subject_logon_id?: string[]; + subject_user_name?: string[]; + target_domain_name?: string[]; + target_logon_id?: string[]; + target_user_name?: string[]; } diff --git a/x-pack/plugins/security_solution/common/ecs/index.ts b/x-pack/plugins/security_solution/common/ecs/index.ts index e31d42b02f80b..b8190463f5da5 100644 --- a/x-pack/plugins/security_solution/common/ecs/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/index.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AgentEcs } from './agent'; import { AuditdEcs } from './auditd'; import { DestinationEcs } from './destination'; import { DnsEcs } from './dns'; import { EndgameEcs } from './endgame'; import { EventEcs } from './event'; +import { FileEcs } from './file'; import { GeoEcs } from './geo'; import { HostEcs } from './host'; import { NetworkEcs } from './network'; @@ -28,6 +30,7 @@ import { SystemEcs } from './system'; export interface Ecs { _id: string; _index?: string; + agent?: AgentEcs; auditd?: AuditdEcs; destination?: DestinationEcs; dns?: DnsEcs; @@ -49,6 +52,6 @@ export interface Ecs { user?: UserEcs; winlog?: WinlogEcs; process?: ProcessEcs; - file?: File; + file?: FileEcs; system?: SystemEcs; } diff --git a/x-pack/plugins/security_solution/common/ecs/process/index.ts b/x-pack/plugins/security_solution/common/ecs/process/index.ts index 0584d95c8059d..451f1455f55d4 100644 --- a/x-pack/plugins/security_solution/common/ecs/process/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/process/index.ts @@ -5,35 +5,25 @@ */ export interface ProcessEcs { + entity_id?: string[]; hash?: ProcessHashData; - pid?: number[]; - name?: string[]; - ppid?: number[]; - args?: string[]; - executable?: string[]; - title?: string[]; - thread?: Thread; - working_directory?: string[]; } export interface ProcessHashData { md5?: string[]; - sha1?: string[]; - sha256?: string[]; } export interface Thread { id?: number[]; - start?: string[]; } diff --git a/x-pack/plugins/security_solution/common/ecs/rule/index.ts b/x-pack/plugins/security_solution/common/ecs/rule/index.ts index c1ef1ee17ca0c..47316c7791e4b 100644 --- a/x-pack/plugins/security_solution/common/ecs/rule/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/rule/index.ts @@ -6,64 +6,38 @@ export interface RuleEcs { id?: string[]; - rule_id?: string[]; - false_positives: string[]; - saved_id?: string[]; - timeline_id?: string[]; - timeline_title?: string[]; - max_signals?: number[]; - risk_score?: string[]; - output_index?: string[]; - description?: string[]; - from?: string[]; - immutable?: boolean[]; - index?: string[]; - interval?: string[]; - language?: string[]; - query?: string[]; - references?: string[]; - severity?: string[]; - tags?: string[]; - threat?: unknown; - + threshold?: { + field: string; + value: number; + }; type?: string[]; - size?: string[]; - to?: string[]; - enabled?: boolean[]; - filters?: unknown; - created_at?: string[]; - updated_at?: string[]; - created_by?: string[]; - updated_by?: string[]; - version?: string[]; - note?: string[]; } diff --git a/x-pack/plugins/security_solution/common/ecs/signal/index.ts b/x-pack/plugins/security_solution/common/ecs/signal/index.ts index 66e35e26af341..55a889f3a5dd1 100644 --- a/x-pack/plugins/security_solution/common/ecs/signal/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/signal/index.ts @@ -8,6 +8,6 @@ import { RuleEcs } from '../rule'; export interface SignalEcs { rule?: RuleEcs; - original_time?: string[]; + status?: string[]; } diff --git a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts index b55226b08b800..48437e12f75a5 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts @@ -113,3 +113,13 @@ export interface GenericBuckets { } export type StringOrNumber = string | number; + +export interface TimerangeFilter { + range: { + [timestamp: string]: { + gte: string; + lte: string; + format: string; + }; + }; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts new file mode 100644 index 0000000000000..0503a9c327467 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common'; +import { Ecs } from '../../../../ecs'; +import { CursorType, Inspect, Maybe } from '../../../common'; +import { TimelineRequestOptionsPaginated } from '../..'; + +export interface TimelineEdges { + node: TimelineItem; + cursor: CursorType; +} + +export interface TimelineItem { + _id: string; + _index?: Maybe; + data: TimelineNonEcsData[]; + ecs: Ecs; +} + +export interface TimelineNonEcsData { + field: string; + value?: Maybe; +} + +export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse { + edges: TimelineEdges[]; + totalCount: number; + pageInfo: { + activePage: number; + totalPages: number; + }; + inspect?: Maybe; +} + +export interface TimelineEventsAllRequestOptions extends TimelineRequestOptionsPaginated { + fields: string[]; + fieldRequested: string[]; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/common/index.ts new file mode 100644 index 0000000000000..200f400ef6816 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/common/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Ecs } from '../../../../ecs'; +import { CursorType, Maybe } from '../../../common'; + +export interface TimelineEdges { + node: TimelineItem; + cursor: CursorType; +} + +export interface TimelineItem { + _id: string; + _index?: Maybe; + data: TimelineNonEcsData[]; + ecs: Ecs; +} + +export interface TimelineNonEcsData { + field: string; + value?: Maybe; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/details/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts similarity index 50% rename from x-pack/plugins/security_solution/common/search_strategy/timeline/details/index.ts rename to x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts index e5e1c41f4731a..6f9192be40150 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/details/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/details/index.ts @@ -4,25 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IEsSearchResponse } from '../../../../../../../src/plugins/data/common'; -import { Inspect, Maybe } from '../../common'; -import { TimelineRequestOptionsPaginated } from '..'; +import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common'; +import { Inspect, Maybe } from '../../../common'; +import { TimelineRequestOptionsPaginated } from '../..'; -export interface DetailItem { +export interface TimelineEventsDetailsItem { field: string; values?: Maybe; // eslint-disable-next-line @typescript-eslint/no-explicit-any originalValue?: Maybe; } -export interface TimelineDetailsStrategyResponse extends IEsSearchResponse { - data?: Maybe; +export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse { + data?: Maybe; inspect?: Maybe; } -export interface TimelineDetailsRequestOptions extends Partial { +export interface TimelineEventsDetailsRequestOptions + extends Partial { defaultIndex: string[]; - executeQuery: boolean; indexName: string; eventId: string; } diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/index.ts new file mode 100644 index 0000000000000..6bb9461995974 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './all'; +export * from './details'; +export * from './last_event_time'; + +export enum TimelineEventsQueries { + all = 'eventsAll', + details = 'eventsDetails', + lastEventTime = 'eventsLastEventTime', +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts new file mode 100644 index 0000000000000..10750503fc807 --- /dev/null +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common'; +import { Inspect, Maybe } from '../../../common'; +import { TimelineRequestBasicOptions } from '../..'; + +export enum LastEventIndexKey { + hostDetails = 'hostDetails', + hosts = 'hosts', + ipDetails = 'ipDetails', + network = 'network', +} + +export interface LastTimeDetails { + hostName?: Maybe; + ip?: Maybe; +} + +export interface TimelineEventsLastEventTimeStrategyResponse extends IEsSearchResponse { + lastSeen: Maybe; + inspect?: Maybe; +} + +export interface TimelineEventsLastEventTimeRequestOptions + extends Omit { + indexKey: LastEventIndexKey; + details: LastTimeDetails; +} diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts index a7bf61c102cd4..773ee60855886 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts @@ -3,45 +3,22 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import { IEsSearchRequest } from '../../../../../../src/plugins/data/common'; import { ESQuery } from '../../typed_json'; -import { Ecs } from '../../ecs'; import { - CursorType, - Maybe, - TimerangeInput, - DocValueFields, - PaginationInput, - PaginationInputPaginated, - SortField, -} from '../common'; -import { TimelineDetailsRequestOptions, TimelineDetailsStrategyResponse } from './details'; - -export * from './details'; + TimelineEventsQueries, + TimelineEventsAllRequestOptions, + TimelineEventsAllStrategyResponse, + TimelineEventsDetailsRequestOptions, + TimelineEventsDetailsStrategyResponse, + TimelineEventsLastEventTimeRequestOptions, + TimelineEventsLastEventTimeStrategyResponse, +} from './events'; +import { DocValueFields, TimerangeInput, SortField } from '../common'; -export enum TimelineQueries { - details = 'details', -} +export * from './events'; -export type TimelineFactoryQueryTypes = TimelineQueries; - -export interface TimelineEdges { - node: TimelineItem; - cursor: CursorType; -} - -export interface TimelineItem { - _id: string; - _index?: Maybe; - data: TimelineNonEcsData[]; - ecs: Ecs; -} - -export interface TimelineNonEcsData { - field: string; - value?: Maybe; -} +export type TimelineFactoryQueryTypes = TimelineEventsQueries; export interface TimelineRequestBasicOptions extends IEsSearchRequest { timerange: TimerangeInput; @@ -51,20 +28,39 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest { factoryQueryType?: TimelineFactoryQueryTypes; } -export interface TimelineRequestOptions extends TimelineRequestBasicOptions { - pagination: PaginationInput; - sortField?: SortField; +export interface TimelineRequestOptions extends TimelineRequestBasicOptions { + pagination: { + activePage: number; + querySize: number; + }; + sort: SortField; } -export interface TimelineRequestOptionsPaginated extends TimelineRequestBasicOptions { - pagination: PaginationInputPaginated; - sortField?: SortField; +export interface TimelineRequestOptionsPaginated + extends TimelineRequestBasicOptions { + pagination: { + activePage: number; + querySize: number; + }; + sort: SortField; } export type TimelineStrategyResponseType< T extends TimelineFactoryQueryTypes -> = T extends TimelineQueries.details ? TimelineDetailsStrategyResponse : never; +> = T extends TimelineEventsQueries.all + ? TimelineEventsAllStrategyResponse + : T extends TimelineEventsQueries.details + ? TimelineEventsDetailsStrategyResponse + : T extends TimelineEventsQueries.lastEventTime + ? TimelineEventsLastEventTimeStrategyResponse + : never; export type TimelineStrategyRequestType< T extends TimelineFactoryQueryTypes -> = T extends TimelineQueries.details ? TimelineDetailsRequestOptions : never; +> = T extends TimelineEventsQueries.all + ? TimelineEventsAllRequestOptions + : T extends TimelineEventsQueries.details + ? TimelineEventsDetailsRequestOptions + : T extends TimelineEventsQueries.lastEventTime + ? TimelineEventsLastEventTimeRequestOptions + : never; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/translations.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/translations.ts index b1ab509417fe5..980ec89f524bc 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/translations.ts @@ -16,7 +16,7 @@ export const ALERTS_DOCUMENT_TYPE = i18n.translate( export const TOTAL_COUNT_OF_ALERTS = i18n.translate( 'xpack.securitySolution.alertsView.totalCountOfAlerts', { - defaultMessage: 'external alerts match the search criteria', + defaultMessage: 'external alerts', } ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 8068d51a80153..074e6faf80c7d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -9,7 +9,7 @@ import React, { useMemo } from 'react'; import styled from 'styled-components'; import { BrowserFields } from '../../containers/source'; -import { DetailItem } from '../../../../common/search_strategy/timeline'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline'; import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model'; import { OnUpdateColumns } from '../../../timelines/components/timeline/events'; import { EventFieldsBrowser } from './event_fields_browser'; @@ -28,7 +28,7 @@ CollapseLink.displayName = 'CollapseLink'; interface Props { browserFields: BrowserFields; columnHeaders: ColumnHeaderOptions[]; - data: DetailItem[]; + data: TimelineEventsDetailsItem[]; id: string; view: View; onEventToggled: () => void; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index 9737a09c89f49..79250ae9bec52 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -10,7 +10,7 @@ import React, { useMemo } from 'react'; import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model'; import { BrowserFields, getAllFieldsByName } from '../../containers/source'; -import { DetailItem } from '../../../../common/search_strategy/timeline'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline'; import { OnUpdateColumns } from '../../../timelines/components/timeline/events'; import { getColumns } from './columns'; @@ -19,7 +19,7 @@ import { search } from './helpers'; interface Props { browserFields: BrowserFields; columnHeaders: ColumnHeaderOptions[]; - data: DetailItem[]; + data: TimelineEventsDetailsItem[]; eventId: string; onUpdateColumns: OnUpdateColumns; timelineId: string; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/stateful_event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/stateful_event_details.tsx index f4028c988acb8..bb74935d5703e 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/stateful_event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/stateful_event_details.tsx @@ -7,7 +7,7 @@ import React, { useCallback, useState } from 'react'; import { BrowserFields } from '../../containers/source'; -import { DetailItem } from '../../../../common/search_strategy/timeline'; +import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline'; import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model'; import { OnUpdateColumns } from '../../../timelines/components/timeline/events'; @@ -16,7 +16,7 @@ import { EventDetails, View } from './event_details'; interface Props { browserFields: BrowserFields; columnHeaders: ColumnHeaderOptions[]; - data: DetailItem[]; + data: TimelineEventsDetailsItem[]; id: string; onEventToggled: () => void; onUpdateColumns: OnUpdateColumns; diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 833688ae57993..037655f594241 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; -import { MockedProvider } from 'react-apollo/test-utils'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; @@ -26,6 +25,11 @@ import { TimelineId } from '../../../../common/types/timeline'; import { KqlMode } from '../../../timelines/store/timeline/model'; import { SortDirection } from '../../../timelines/components/timeline/body/sort'; import { AlertsTableFilterGroup } from '../../../detections/components/alerts_table/alerts_filter_group'; +import { useTimelineEvents } from '../../../timelines/containers'; + +jest.mock('../../../timelines/containers', () => ({ + useTimelineEvents: jest.fn(), +})); jest.mock('../../components/url_state/normalize_time_range.ts'); @@ -83,20 +87,19 @@ describe('EventsViewer', () => { const mount = useMountAppended(); beforeEach(() => { + (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); mockUseFetchIndexPatterns.mockImplementation(() => [{ ...defaultMocks }]); }); test('it renders the "Showing..." subtitle with the expected event count', async () => { const wrapper = mount( - - - + ); @@ -114,14 +117,12 @@ describe('EventsViewer', () => { const wrapper = mount( - - - + ); @@ -139,14 +140,12 @@ describe('EventsViewer', () => { const wrapper = mount( - - - + ); @@ -164,14 +163,12 @@ describe('EventsViewer', () => { const wrapper = mount( - - - + ); @@ -187,14 +184,12 @@ describe('EventsViewer', () => { test('it renders the Fields Browser as a settings gear', async () => { const wrapper = mount( - - - + ); @@ -208,21 +203,19 @@ describe('EventsViewer', () => { test('it renders the footer containing the Load More button', async () => { const wrapper = mount( - - - + ); await waitFor(() => { wrapper.update(); - expect(wrapper.find(`[data-test-subj="TimelineMoreButton"]`).first().exists()).toBe(true); + expect(wrapper.find(`[data-test-subj="timeline-pagination"]`).first().exists()).toBe(true); }); }); @@ -230,14 +223,12 @@ describe('EventsViewer', () => { test(`it renders the ${header.id} default EventsViewer column header`, async () => { const wrapper = mount( - - - + ); @@ -257,13 +248,11 @@ describe('EventsViewer', () => { test('it renders the provided headerFilterGroup', async () => { const wrapper = mount( - - } - /> - + } + /> ); @@ -277,13 +266,11 @@ describe('EventsViewer', () => { test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is undefined', async () => { const wrapper = mount( - - } - /> - + } + /> ); @@ -299,13 +286,11 @@ describe('EventsViewer', () => { test('it has a visible HeaderFilterGroupWrapper when Resolver is NOT showing, because graphEventId is an empty string', async () => { const wrapper = mount( - - } - /> - + } + /> ); @@ -321,13 +306,11 @@ describe('EventsViewer', () => { test('it does NOT have a visible HeaderFilterGroupWrapper when Resolver is showing, because graphEventId is a valid id', async () => { const wrapper = mount( - - } - /> - + } + /> ); @@ -343,13 +326,11 @@ describe('EventsViewer', () => { test('it (still) renders an invisible headerFilterGroup (to maintain state while hidden) when Resolver is showing, because graphEventId is a valid id', async () => { const wrapper = mount( - - } - /> - + } + /> ); @@ -365,9 +346,7 @@ describe('EventsViewer', () => { test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', async () => { const wrapper = mount( - - - + ); @@ -381,9 +360,7 @@ describe('EventsViewer', () => { test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is an empty string', async () => { const wrapper = mount( - - - + ); @@ -397,9 +374,7 @@ describe('EventsViewer', () => { test('it does NOT render the provided utilityBar when Resolver is showing, because graphEventId is a valid id', async () => { const wrapper = mount( - - - + ); @@ -415,9 +390,7 @@ describe('EventsViewer', () => { test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', async () => { const wrapper = mount( - - - + ); @@ -431,9 +404,7 @@ describe('EventsViewer', () => { test('it renders the inspect button when Resolver is NOT showing, because graphEventId is an empty string', async () => { const wrapper = mount( - - - + ); @@ -447,9 +418,7 @@ describe('EventsViewer', () => { test('it does NOT render the inspect button when Resolver is showing, because graphEventId is a valid id', async () => { const wrapper = mount( - - - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 3d193856a8ae4..2998bd031d674 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -10,9 +10,9 @@ import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; +import { Direction } from '../../../../common/search_strategy'; import { BrowserFields, DocValueFields } from '../../containers/source'; -import { TimelineQuery } from '../../../timelines/containers'; -import { Direction } from '../../../graphql/types'; +import { useTimelineEvents } from '../../../timelines/containers'; import { useKibana } from '../../lib/kibana'; import { ColumnHeaderOptions, KqlMode } from '../../../timelines/store/timeline/model'; import { HeaderSection } from '../header_section'; @@ -196,14 +196,51 @@ const EventsViewerComponent: React.FC = ({ ), [columnsHeader, queryFields] ); + const sortField = useMemo( () => ({ - sortFieldId: sort.columnId, + field: sort.columnId, direction: sort.sortDirection as Direction, }), [sort.columnId, sort.sortDirection] ); + const [ + loading, + { events, updatedAt, inspect, loadPage, pageInfo, refetch, totalCount = 0 }, + ] = useTimelineEvents({ + docValueFields, + fields, + filterQuery: combinedQueries!.filterQuery, + id, + indexPattern, + limit: itemsPerPage, + sort: sortField, + startDate: start, + endDate: end, + skip: !canQueryTimeline, + }); + + const totalCountMinusDeleted = useMemo( + () => (totalCount > 0 ? totalCount - deletedEventIds.length : 0), + [deletedEventIds.length, totalCount] + ); + + const subtitle = useMemo( + () => + `${i18n.SHOWING}: ${totalCountMinusDeleted.toLocaleString()} ${unit(totalCountMinusDeleted)}`, + [totalCountMinusDeleted, unit] + ); + + const nonDeletedEvents = useMemo(() => events.filter((e) => !deletedEventIds.includes(e._id)), [ + deletedEventIds, + events, + ]); + + useEffect(() => { + setIsQueryLoading(loading); + }, [loading]); + return ( = ({ > {canQueryTimeline ? ( - - {({ - events, - getUpdatedAt, - inspect, - loading, - loadMore, - pageInfo, - refetch, - totalCount = 0, - }) => { - setIsQueryLoading(loading); - const totalCountMinusDeleted = - totalCount > 0 ? totalCount - deletedEventIds.length : 0; - - const subtitle = `${i18n.SHOWING}: ${totalCountMinusDeleted.toLocaleString()} ${unit( - totalCountMinusDeleted - )}`; - - return ( - <> - - {headerFilterGroup && ( - - {headerFilterGroup} - - )} - - {utilityBar && !resolverIsShowing(graphEventId) && ( - {utilityBar?.(refetch, totalCountMinusDeleted)} - )} - - + <> + + {headerFilterGroup && ( + + {headerFilterGroup} + + )} + + {utilityBar && !resolverIsShowing(graphEventId) && ( + {utilityBar?.(refetch, totalCountMinusDeleted)} + )} + + - !deletedEventIds.includes(e._id))} - docValueFields={docValueFields} - id={id} - isEventViewer={true} - refetch={refetch} - sort={sort} - toggleColumn={toggleColumn} - /> + - { - /** Hide the footer if Resolver is showing. */ - !graphEventId && ( -