From ea762092718a8631d9a6489dbb65666bdafc39f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Wed, 16 Sep 2020 20:25:10 +0200 Subject: [PATCH 1/5] [Security Solution] Refactor Timeline Events to use Search Strategy (#77205) --- .../common/ecs/agent/index.ts | 9 + .../common/ecs/endgame/index.ts | 38 +- .../security_solution/common/ecs/index.ts | 5 +- .../common/ecs/process/index.ts | 12 +- .../common/ecs/rule/index.ts | 34 +- .../common/ecs/signal/index.ts | 2 +- .../common/search_strategy/common/index.ts | 10 + .../timeline/events/all/index.ts | 42 ++ .../timeline/events/common/index.ts | 25 ++ .../timeline/{ => events}/details/index.ts | 16 +- .../search_strategy/timeline/events/index.ts | 15 + .../timeline/events/last_event_time/index.ts | 32 ++ .../common/search_strategy/timeline/index.ts | 80 ++-- .../components/alerts_viewer/translations.ts | 2 +- .../event_details/event_details.tsx | 4 +- .../event_details/event_fields_browser.tsx | 4 +- .../event_details/stateful_event_details.tsx | 4 +- .../events_viewer/events_viewer.test.tsx | 191 ++++----- .../events_viewer/events_viewer.tsx | 199 ++++----- .../components/events_viewer/index.test.tsx | 36 +- .../common/components/events_viewer/mock.ts | 68 +-- .../add_exception_modal/index.test.tsx | 3 +- .../exceptions/add_exception_modal/index.tsx | 3 +- .../common/components/exceptions/helpers.tsx | 2 +- .../components/last_event_time/index.test.tsx | 55 +-- .../components/last_event_time/index.tsx | 12 +- .../events/last_event_time/index.ts | 197 +++++---- .../events/last_event_time/translations.ts | 21 + .../public/common/mock/mock_ecs.ts | 265 +----------- .../common/mock/mock_endgame_ecs_data.ts | 2 +- .../public/common/mock/mock_timeline_data.ts | 350 +-------------- .../public/common/mock/netflow.ts | 2 +- .../components/alerts_table/actions.test.tsx | 3 +- .../components/alerts_table/actions.tsx | 24 +- .../alerts_table/alerts_utility_bar/index.tsx | 2 +- .../timeline_actions/alert_context_menu.tsx | 3 +- .../investigate_in_timeline_action.tsx | 3 +- .../components/alerts_table/translations.ts | 2 +- .../components/alerts_table/types.ts | 4 +- .../public/hosts/containers/hosts/index.tsx | 11 +- .../components/flyout/header/index.tsx | 2 - .../timeline/body/column_headers/index.tsx | 9 +- .../body/data_driven_columns/index.tsx | 3 +- .../body/events/event_column_view.tsx | 3 +- .../components/timeline/body/events/index.tsx | 5 +- .../timeline/body/events/stateful_event.tsx | 18 +- .../components/timeline/body/helpers.test.ts | 9 +- .../components/timeline/body/helpers.tsx | 3 +- .../components/timeline/body/index.test.tsx | 3 +- .../components/timeline/body/index.tsx | 6 +- .../generic_details.test.tsx.snap | 19 - .../generic_file_details.test.tsx.snap | 23 - .../generic_row_renderer.test.tsx.snap | 37 -- .../body/renderers/auditd/generic_details.tsx | 2 +- .../renderers/auditd/generic_file_details.tsx | 2 +- .../auditd/generic_row_renderer.test.tsx | 2 +- .../body/renderers/column_renderer.ts | 2 +- .../dns/dns_request_event_details.tsx | 2 +- .../renderers/empty_column_renderer.test.tsx | 2 +- .../body/renderers/empty_column_renderer.tsx | 2 +- .../endgame_security_event_details.tsx | 2 +- .../renderers/get_column_renderer.test.tsx | 2 +- .../body/renderers/get_column_renderer.ts | 2 +- .../body/renderers/get_row_renderer.test.tsx | 2 +- .../body/renderers/get_row_renderer.ts | 2 +- .../timeline/body/renderers/helpers.test.tsx | 2 +- .../timeline/body/renderers/helpers.tsx | 2 +- .../timeline/body/renderers/netflow.tsx | 2 +- .../netflow/netflow_row_renderer.test.tsx | 2 +- .../renderers/plain_column_renderer.test.tsx | 2 +- .../body/renderers/plain_column_renderer.tsx | 2 +- .../renderers/plain_row_renderer.test.tsx | 2 +- .../timeline/body/renderers/row_renderer.tsx | 2 +- .../renderers/suricata/suricata_details.tsx | 2 +- .../suricata/suricata_row_renderer.test.tsx | 2 +- .../generic_details.test.tsx.snap | 34 -- .../generic_file_details.test.tsx.snap | 35 -- .../generic_row_renderer.test.tsx.snap | 68 --- .../body/renderers/system/generic_details.tsx | 2 +- .../renderers/system/generic_file_details.tsx | 2 +- .../system/generic_row_renderer.test.tsx | 2 +- .../unknown_column_renderer.test.tsx | 2 +- .../body/renderers/zeek/zeek_details.tsx | 2 +- .../renderers/zeek/zeek_row_renderer.test.tsx | 2 +- .../renderers/zeek/zeek_signature.test.tsx | 2 +- .../body/renderers/zeek/zeek_signature.tsx | 2 +- .../timeline/body/stateful_body.tsx | 4 +- .../timelines/components/timeline/events.ts | 2 +- .../timeline/expandable_event/index.tsx | 4 +- .../footer/__snapshots__/index.test.tsx.snap | 9 +- .../components/timeline/footer/index.test.tsx | 108 +++-- .../components/timeline/footer/index.tsx | 78 ++-- .../timeline/footer/translations.ts | 6 +- .../components/timeline/index.test.tsx | 12 +- .../components/timeline/timeline.test.tsx | 105 ++--- .../components/timeline/timeline.tsx | 151 +++---- .../timelines/containers/details/index.tsx | 72 ++-- .../public/timelines/containers/index.tsx | 400 ++++++++++-------- .../timelines/containers/translations.ts | 21 + .../timelines/store/timeline/actions.ts | 2 +- .../timelines/store/timeline/helpers.ts | 2 +- .../public/timelines/store/timeline/model.ts | 12 +- .../search_strategy/helpers/to_array.ts | 2 +- .../factory/hosts/all/index.ts | 2 +- .../factory/hosts/all/query.all_hosts.dsl.ts | 4 +- .../hosts/authentications/__mocks__/index.ts | 81 +++- .../factory/hosts/authentications/index.tsx | 2 +- .../factory/hosts/overview/__mocks__/index.ts | 219 +++++++++- .../uncommon_processes/__mocks__/index.ts | 144 ++++++- .../factory/hosts/uncommon_processes/index.ts | 2 +- .../matrix_histogram/__mocks__/index.ts | 331 ++++++++++++++- .../alerts/__mocks__/index.ts | 23 +- .../anomalies/__mocks__/index.ts | 16 +- .../authentications/__mocks__/index.ts | 2 +- .../matrix_histogram/dns/__mocks__/index.ts | 2 +- .../events/__mocks__/index.ts | 2 +- .../factory/network/dns/__mocks__/index.ts | 56 ++- .../factory/network/dns/index.ts | 2 +- .../factory/network/http/__mocks__/index.ts | 55 ++- .../factory/network/http/index.ts | 2 +- .../network/overview/__mocks__/index.ts | 91 +++- .../factory/network/tls/__mocks__/index.ts | 56 ++- .../factory/network/tls/index.ts | 2 +- .../network/top_countries/__mocks__/index.ts | 61 ++- .../factory/network/top_countries/index.ts | 2 +- .../network/top_n_flow/__mocks__/index.ts | 68 ++- .../factory/network/top_n_flow/index.ts | 2 +- .../factory/network/users/__mocks__/index.ts | 57 ++- .../factory/network/users/index.ts | 2 +- .../timeline/factory/events/all/constants.ts | 219 ++++++++++ .../timeline/factory/events/all/helpers.ts | 92 ++++ .../timeline/factory/events/all/index.ts | 68 +++ .../events/all/query.events_all.dsl.ts | 76 ++++ .../factory/{ => events}/details/helpers.ts | 10 +- .../factory/{ => events}/details/index.ts | 24 +- .../details/query.events_details.dsl.ts} | 2 +- .../timeline/factory/events/index.ts | 24 ++ .../factory/events/last_event_time/index.ts | 36 ++ .../query.events_last_event_time.dsl.ts | 93 ++++ .../search_strategy/timeline/factory/index.ts | 10 +- .../server/utils/build_query/filters.ts | 4 +- .../server/utils/build_query/index.ts | 1 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 144 files changed, 3081 insertions(+), 1985 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/ecs/agent/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/timeline/events/common/index.ts rename x-pack/plugins/security_solution/common/search_strategy/timeline/{ => events}/details/index.ts (50%) create mode 100644 x-pack/plugins/security_solution/common/search_strategy/timeline/events/index.ts create mode 100644 x-pack/plugins/security_solution/common/search_strategy/timeline/events/last_event_time/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/containers/events/last_event_time/translations.ts create mode 100644 x-pack/plugins/security_solution/public/timelines/containers/translations.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/constants.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/helpers.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts rename x-pack/plugins/security_solution/server/search_strategy/timeline/factory/{ => events}/details/helpers.ts (81%) rename x-pack/plugins/security_solution/server/search_strategy/timeline/factory/{ => events}/details/index.ts (58%) rename x-pack/plugins/security_solution/server/search_strategy/timeline/factory/{details/query.timeline_details.dsl.ts => events/details/query.events_details.dsl.ts} (88%) create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/last_event_time/index.ts create mode 100644 x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts 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 && ( -