Skip to content

Commit

Permalink
[Security Solution] Decompose Timelines TGrid component and moved to …
Browse files Browse the repository at this point in the history
…security_solution (#140151)

Resolves [#143152](#143152)
### Observability changes
This changes is a result of removal some types from `timelines` plugin:
- cleaned up timelines plugin related types, 
- replaced `Pick<ActionProps,'data' | 'eventId' | 'ecsData' |
'setEventsDeleted' >` with the props which were actually used:
  
    ```
  data: TimelineNonEcsData[];
    ecsData: Ecs;
    eventId: string;
  ```
In this PR we still have references to `@kbn/timelines-plugin`, which
needs to be changed later.
Threat Hunting team are going to think about replacing
`TimelineNonEcsData` with the other type definition (maybe
`NonEcsData`?) and moving `Ecs` type to the non `timelines` related
plugin/package.

### Security Solution changes
Before the current PR changes the components dependencies around `TGrid`
looked like the image below:
<img width="848" alt="Screen Shot 2022-11-29 at 6 16 14 AM"
src="https://user-images.githubusercontent.com/55110838/204663019-664431fb-f360-4a11-b395-6fa54c35dd6d.png">
After decomposition the `timelines` plugin hosted TGrid HOC and moving
all the data tables related sub-components to `security_solution` plugin
the new components architecture got the next shape:
<img width="842" alt="Screen Shot 2022-11-29 at 6 14 41 AM"
src="https://user-images.githubusercontent.com/55110838/204663068-40897f18-1485-4b59-a71b-ce09e660f7db.png">
`security_solution` plugin changes includes the next things:
- Moved some data table and actions types to
`x-pack/plugins/security_solution/common/types`, which is widely used
across the related components.
- Due to the movement of the data table with the store to from
`timeline` plugin to `security_solution` many test files which had the
reference to `tGridReducer` now cleaned up from the unnecessary logic:
```
- import { tGridReducer } from '@kbn/timelines-plugin/public';
```

and `TableState` references was replaced with the next changes:
```
- import type { TableState } from '@kbn/timelines-plugin/public';
+ import type { TableState } from '../common/store/data_table/types';
```
- Replaced `tGridActions` with `dataTableActions` name.
- Moved `control_columns` to `security_solution` common plugin
components: `RowCheckBox`, `HeaderCheckBox` and
`transformControlColumns`:
`RowActionComponent` moved from `timelines` plugin to
`x-pack/plugins/security_solution/public/common/components/control_columns/row_action`
without changes.
`transformControlColumns` moved from timelines plugin to
`x-pack/plugins/security_solution/public/common/components/control_columns/transform_control_columns.tsx`.
Removed not used property `hasAlertsCrudPermissions`, added unit test.
<img width="1222" alt="Screen Shot 2022-11-29 at 8 59 42 PM"
src="https://user-images.githubusercontent.com/55110838/204711499-9f90fee2-3c2f-4ff6-af28-c324ab1840d8.png">

- Many translation changes as a result of the owner plugin change:
 ```
 - i18n.translate('xpack.timelines....', {
 + i18n.translate('xpack.securitySolution....', {
```
- Moved `useDraggableKeyboardWrapper` to security_solution, added reference to `useAddToTimeline`, by using timelines plugin with kibana services. Added unit tests. 
<img width="1112" alt="Screen Shot 2022-11-30 at 9 06 42 AM" src="https://user-images.githubusercontent.com/55110838/204862298-bcd50a52-dbf7-480b-bf13-8e48d6835746.png">

- Replaced the next references:
```
- type: 'x-pack/timelines/t-grid/UPDATE_COLUMN_WIDTH',
+ type: 'x-pack/security_solution/data-table/UPDATE_COLUMN_WIDTH',
```

```
- type: 'x-pack/timelines/t-grid/REMOVE_COLUMN',
+ type: 'x-pack/security_solution/data-table/REMOVE_COLUMN',
```
- moved TGrid store previously hosted in timeline plugin  to `security_solution` as `data_table` store:
<img width="1109" alt="Screen Shot 2022-11-29 at 9 24 08 PM" src="https://user-images.githubusercontent.com/55110838/204714668-257a9c50-d722-4a6d-9214-f3ef8a14d0d2.png">

- Migrated TGrid `BodyComponent` to `DataTableComponent`
`x-pack/plugins/security_solution/public/common/components/data_table/index.tsx`
Removed some unused properties: `hasAlertsCrudPermissions, appId, getRowRenderer, isEventViewer, tableView, totalSelectAllAlerts, trailingControlColumns`. Current DataTableComponent is a subset of the previous BodyComponent, which includes only table related functionality:
<img width="1028" alt="Screen Shot 2022-11-30 at 10 44 35 AM" src="https://user-images.githubusercontent.com/55110838/204882561-0950b9ce-5a9f-4bdb-b38f-6ff742fc3f92.png">

- Renamed `TimelineExpandedDetail` to `ExpandedDetail` to make the type more generic for usage.
- BulkActions related changes includes:
<img width="1288" alt="Screen Shot 2022-11-29 at 9 13 32 PM" src="https://user-images.githubusercontent.com/55110838/204713196-409f3d5e-f752-4fe9-9ae9-e752514cbf99.png">

`AlertBulkActionsComponent` moved from timelines plugin to `x-pack/plugins/security_solution/public/common/components/toolbar/bulk_actions/alert_bulk_actions.tsx`, just renaming changes.

Added `x-pack/plugins/security_solution/public/common/components/toolbar/bulk_actions/types.ts` to consolidate types
`useBulkActionItems` moved from timelines plugin to `x-pack/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_bulk_action_items.tsx`. Changed links, renamed `AlertsStatus` to `AlertWorkflowStatus`, removed `in-progress` case handling.

`useUpdateAlertsStatus` moved from timelines plugin to `x-pack/plugins/security_solution/public/common/components/toolbar/bulk_actions/use_update_alerts.ts`. Cleaned up the code from handling Observability API.
- Updated `x-pack/plugins/security_solution/public/common/lib/kuery/index.ts` with the actual implementations of 
```
  convertKueryToDslFilter,
  convertKueryToElasticSearchQuery,
  convertToBuildEsQuery,
  escapeKuery,
  escapeQueryValue,
  combineQueries,
```
instead of referencing timelines plugin.
- Moved `EventRenderedView` component to security_solution common components. Later planning to make it as a package.
- `EventsViewer` component became the stateful component which is responsible for the data representation managing. Some part from TGridIntegratedComponent and BodyComponent was merged under its logic:
<img width="1052" alt="Screen Shot 2022-11-30 at 6 22 22 PM" src="https://user-images.githubusercontent.com/55110838/204950708-a8875acd-eb62-4df5-8ac4-613a0a571de6.png">
<img width="242" alt="Screen Shot 2022-11-30 at 6 24 06 PM" src="https://user-images.githubusercontent.com/55110838/204950819-bca194a4-4309-4cb4-a2ba-0176e9fe6c65.png">

- Moved header actions  to common components `x-pack/plugins/security_solution/public/common/components/header_actions`
- Renamed component `AlertCount` to `UnitCount`.
- Moved to `security_solution` configuration for `APM_USER_INTERACTIONS`
- changes `createStore` interface by using the direct reference to `dataTableReducer` instead of passing down it's value through the params.
### Timeline plugin changes
- cleaned up timeline plugin interface by removing:
```
getTGrid: <T extends TGridType = 'embedded'>(
    props: GetTGridProps<T>
  ) => ReactElement<GetTGridProps<T>>;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
getTGridReducer: () => any;
getUseDraggableKeyboardWrapper: () => (
    props: UseDraggableKeyboardWrapperProps
  ) => UseDraggableKeyboardWrapper;
```
- renamed embedded store
```
- setTGridEmbeddedStore: (store: Store) => void;
+ setTimelineEmbeddedStore: (store: Store) => void;
```
- removed dependency to triggers_actions_ui plugin
- removed duplicated components and types with `security_solution`: 
```
TruncatableText
SubtitleComponent
EventsCountComponent
PopoverRowItems
PagingControlComponent
FooterComponent
SortIndicator
SortNumber
RowRendererContainer
plainRowRenderer
getColumnRenderer
StatefulRowRenderer
getMappedNonEcsValue
InspectButtonComponent
TGridCellAction
useMountAppended
tgrid store
```

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
YulNaumenko and kibanamachine committed Dec 6, 2022
1 parent 3e49992 commit f1dc15a
Show file tree
Hide file tree
Showing 338 changed files with 4,607 additions and 12,477 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ describe('ObservabilityActions component', () => {
},
data: inventoryThresholdAlert as unknown as TimelineNonEcsData[],
observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(),
setEventsDeleted: jest.fn(),
setFlyoutAlert: jest.fn(),
id: pageId,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import React, { useMemo, useState, useCallback } from 'react';

import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public';
import { CommentType } from '@kbn/cases-plugin/common';
import type { ActionProps } from '@kbn/timelines-plugin/common';
import { Ecs } from '@kbn/timelines-plugin/common/ecs';
import { TimelineNonEcsData } from '@kbn/timelines-plugin/common';
import { isAlertDetailsEnabledPerApp } from '../../../utils/is_alert_details_enabled';
import { useKibana } from '../../../utils/kibana_react';
import { useGetUserCasesPermissions } from '../../../hooks/use_get_user_cases_permissions';
Expand All @@ -32,15 +33,15 @@ import { ObservabilityRuleTypeRegistry } from '../../..';
import { ALERT_DETAILS_PAGE_ID } from '../../alert_details/types';
import { ConfigSchema } from '../../../plugin';

export type ObservabilityActionsProps = Pick<
ActionProps,
'data' | 'eventId' | 'ecsData' | 'setEventsDeleted'
> & {
export interface ObservabilityActionsProps {
data: TimelineNonEcsData[];
ecsData: Ecs;
eventId: string;
setFlyoutAlert: React.Dispatch<React.SetStateAction<TopAlert | undefined>>;
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry;
id?: string;
config: ConfigSchema;
};
}

export function ObservabilityActions({
data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const buildData = (alerts: EcsFieldsResponse): ObservabilityActionsProps['data']
[]
);
};
const fakeSetEventsDeleted = () => [];
export const getRowActions = (
observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry,
config: ConfigSchema
Expand All @@ -35,7 +34,6 @@ export const getRowActions = (
ecsData={{ _id: alert._id, _index: alert._index }}
id={id}
observabilityRuleTypeRegistry={observabilityRuleTypeRegistry}
setEventsDeleted={fakeSetEventsDeleted}
setFlyoutAlert={setFlyoutAlert}
config={config}
/>
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/security_solution/common/ecs/signal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ export type SignalEcsAAD = Exclude<SignalEcs, 'rule' | 'status'> & {
severity?: string[];
building_block_type?: string[];
workflow_status?: string[];
suppression?: {
docs_count: string[];
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 type { TimelineItem } from '../../search_strategy';
export interface CustomBulkAction {
key: string;
label: string;
disableOnQuery?: boolean;
disabledLabel?: string;
onClick: (items?: TimelineItem[]) => void;
['data-test-subj']?: string;
}

export type CustomBulkActionProp = Omit<CustomBulkAction, 'onClick'> & {
onClick: (eventIds: string[]) => void;
};
49 changes: 49 additions & 0 deletions x-pack/plugins/security_solution/common/types/data_table/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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 * as runtimeTypes from 'io-ts';

export enum Direction {
asc = 'asc',
desc = 'desc',
}

export type SortDirectionTable = 'none' | 'asc' | 'desc' | Direction;
export interface SortColumnTable {
columnId: string;
columnType: string;
esTypes?: string[];
sortDirection: SortDirectionTable;
}

export type { TableById } from '../../../public/common/store/data_table/types';

export enum TableId {
usersPageEvents = 'users-page-events',
hostsPageEvents = 'hosts-page-events',
networkPageEvents = 'network-page-events',
hostsPageSessions = 'hosts-page-sessions-v2', // the v2 is to cache bust localstorage settings as default columns were reworked.
alertsOnRuleDetailsPage = 'alerts-rules-details-page',
alertsOnAlertsPage = 'alerts-page',
test = 'table-test', // Reserved for testing purposes
alternateTest = 'alternateTest',
rulePreview = 'rule-preview',
kubernetesPageSessions = 'kubernetes-page-sessions',
}

const TableIdLiteralRt = runtimeTypes.union([
runtimeTypes.literal(TableId.usersPageEvents),
runtimeTypes.literal(TableId.hostsPageEvents),
runtimeTypes.literal(TableId.networkPageEvents),
runtimeTypes.literal(TableId.hostsPageSessions),
runtimeTypes.literal(TableId.alertsOnRuleDetailsPage),
runtimeTypes.literal(TableId.alertsOnAlertsPage),
runtimeTypes.literal(TableId.test),
runtimeTypes.literal(TableId.rulePreview),
runtimeTypes.literal(TableId.kubernetesPageSessions),
]);
export type TableIdLiteral = runtimeTypes.TypeOf<typeof TableIdLiteralRt>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 type { FlowTargetSourceDest } from '../../search_strategy';
import type { TimelineTabs } from '../timeline';

type EmptyObject = Record<string | number, never>;

export type ExpandedEventType =
| {
panelView?: 'eventDetail';
params?: {
eventId: string;
indexName: string;
refetch?: () => void;
};
}
| EmptyObject;

export type ExpandedHostType =
| {
panelView?: 'hostDetail';
params?: {
hostName: string;
};
}
| EmptyObject;

export type ExpandedNetworkType =
| {
panelView?: 'networkDetail';
params?: {
ip: string;
flowTarget: FlowTargetSourceDest;
};
}
| EmptyObject;

export type ExpandedUserType =
| {
panelView?: 'userDetail';
params?: {
userName: string;
};
}
| EmptyObject;

export type ExpandedDetailType =
| ExpandedEventType
| ExpandedHostType
| ExpandedNetworkType
| ExpandedUserType;

export type ExpandedDetailTimeline = {
[tab in TimelineTabs]?: ExpandedDetailType;
};

export type ExpandedDetail = Partial<Record<string, ExpandedDetailType>>;
175 changes: 175 additions & 0 deletions x-pack/plugins/security_solution/common/types/header_actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* 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 type {
EuiDataGridCellValueElementProps,
EuiDataGridColumn,
EuiDataGridColumnCellActionProps,
EuiDataGridControlColumn,
} from '@elastic/eui';
import type { IFieldSubType } from '@kbn/es-query';
import type { FieldBrowserOptions } from '@kbn/triggers-actions-ui-plugin/public';
import type { ComponentType, JSXElementConstructor, ReactNode } from 'react';
import type { OnRowSelected, SetEventsDeleted, SetEventsLoading } from '..';
import type { Ecs } from '../../ecs';
import type { BrowserFields, TimelineNonEcsData } from '../../search_strategy';
import type { SortColumnTable } from '../data_table';

export type ColumnHeaderType = 'not-filtered' | 'text-filter';

/** Uniquely identifies a column */
export type ColumnId = string;

/**
* A `DataTableCellAction` function accepts `data`, where each row of data is
* represented as a `TimelineNonEcsData[]`. For example, `data[0]` would
* contain a `TimelineNonEcsData[]` with the first row of data.
*
* A `DataTableCellAction` returns a function that has access to all the
* `EuiDataGridColumnCellActionProps`, _plus_ access to `data`,
* which enables code like the following example to be written:
*
* Example:
* ```
* ({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => {
* const value = getMappedNonEcsValue({
* data: data[rowIndex], // access a specific row's values
* fieldName: columnId,
* });
*
* return (
* <Component onClick={() => alert(`row ${rowIndex} col ${columnId} has value ${value}`)} iconType="heart">
* {'Love it'}
* </Component>
* );
* };
* ```
*/
export type DataTableCellAction = ({
browserFields,
data,
ecsData,
header,
pageSize,
scopeId,
closeCellPopover,
}: {
browserFields: BrowserFields;
/** each row of data is represented as one TimelineNonEcsData[] */
data: TimelineNonEcsData[][];
ecsData: Ecs[];
header?: ColumnHeaderOptions;
pageSize: number;
scopeId: string;
closeCellPopover?: () => void;
}) => (props: EuiDataGridColumnCellActionProps) => ReactNode;

/** The specification of a column header */
export type ColumnHeaderOptions = Pick<
EuiDataGridColumn,
| 'actions'
| 'defaultSortDirection'
| 'display'
| 'displayAsText'
| 'id'
| 'initialWidth'
| 'isSortable'
| 'schema'
> & {
aggregatable?: boolean;
dataTableCellActions?: DataTableCellAction[];
category?: string;
columnHeaderType: ColumnHeaderType;
description?: string | null;
esTypes?: string[];
example?: string | number | null;
format?: string;
linkField?: string;
placeholder?: string;
subType?: IFieldSubType;
type?: string;
};
export interface HeaderActionProps {
width: number;
browserFields: BrowserFields;
columnHeaders: ColumnHeaderOptions[];
fieldBrowserOptions?: FieldBrowserOptions;
isEventViewer?: boolean;
isSelectAllChecked: boolean;
onSelectAll: ({ isSelected }: { isSelected: boolean }) => void;
showEventsSelect: boolean;
showSelectAllCheckbox: boolean;
sort: SortColumnTable[];
tabType: string;
timelineId: string;
}

export type HeaderCellRender = ComponentType | ComponentType<HeaderActionProps>;

type GenericActionRowCellRenderProps = Pick<
EuiDataGridCellValueElementProps,
'rowIndex' | 'columnId'
>;

export type RowCellRender =
| JSXElementConstructor<GenericActionRowCellRenderProps>
| ((props: GenericActionRowCellRenderProps) => JSX.Element)
| JSXElementConstructor<ActionProps>
| ((props: ActionProps) => JSX.Element);

export interface ActionProps {
action?: RowCellRender;
ariaRowindex: number;
checked: boolean;
columnId: string;
columnValues: string;
data: TimelineNonEcsData[];
disabled?: boolean;
ecsData: Ecs;
eventId: string;
eventIdToNoteIds?: Readonly<Record<string, string[]>>;
index: number;
isEventPinned?: boolean;
isEventViewer?: boolean;
loadingEventIds: Readonly<string[]>;
onEventDetailsPanelOpened: () => void;
onRowSelected: OnRowSelected;
onRuleChange?: () => void;
refetch?: () => void;
rowIndex: number;
setEventsDeleted: SetEventsDeleted;
setEventsLoading: SetEventsLoading;
showCheckboxes: boolean;
showNotes?: boolean;
tabType?: string;
timelineId: string;
toggleShowNotes?: () => void;
width?: number;
}

interface AdditionalControlColumnProps {
ariaRowindex: number;
actionsColumnWidth: number;
columnValues: string;
checked: boolean;
onRowSelected: OnRowSelected;
eventId: string;
id: string;
columnId: string;
loadingEventIds: Readonly<string[]>;
onEventDetailsPanelOpened: () => void;
showCheckboxes: boolean;
// Override these type definitions to support either a generic custom component or the one used in security_solution today.
headerCellRender: HeaderCellRender;
rowCellRender: RowCellRender;
}

export type ControlColumnProps = Omit<
EuiDataGridControlColumn,
keyof AdditionalControlColumnProps
> &
Partial<AdditionalControlColumnProps>;
14 changes: 14 additions & 0 deletions x-pack/plugins/security_solution/common/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,18 @@
* 2.0.
*/

import type { Status } from '../detection_engine/schemas/common';

export * from './timeline';
export * from './data_table';
export * from './detail_panel';
export * from './header_actions';
export * from './session_view';
export * from './bulk_actions';

export const FILTER_OPEN: Status = 'open';
export const FILTER_CLOSED: Status = 'closed';
export const FILTER_ACKNOWLEDGED: Status = 'acknowledged';

export type SetEventsLoading = (params: { eventIds: string[]; isLoading: boolean }) => void;
export type SetEventsDeleted = (params: { eventIds: string[]; isDeleted: boolean }) => void;
Loading

0 comments on commit f1dc15a

Please sign in to comment.