mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution][Tech Debt] Decoupled TGrid state part from Timelines under the security_solution store (#141010)
* [Security Solution][Tech Debt] Decoupled TGrid state part from Timelines under the security_solution store * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Unified usage of data table get by id selector * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Cleanup - removed not used code * - * - * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixed add to timeline * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixed filter manager for useHoverActions by proper context usage for defining the scopeId * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixed es lint * - * TableIds to TableId * Fixed unit tests * Fixed tests * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * - * fixed garphevent component * FIxed details tests * Added mock for cases test * Fixed store tests * fixed mocks * fixed mocks * Cleaned up tgrid store from the timeline actions * Set back reduceReducers to handle ability addToTimelineButton, need to change this later when timelines data will live in the timeline plugin * fixed merge * fixed check types * Fixed type checks * Fixed tests * Added snapshot * Fixed toggleDetails for user and host * fixed tests * Fixed timelines tests * FIxed tests * Fixed tests * Fixed tests * Fixed Jest tests * Fixed resolver bug * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * FIxed miissing filterManager * moved tgrid store * Reduced bundle size! * Fixed names * Fixed tests * Removed test * New securitySolution bundle size * Cleanup the store * More cleanup * Removed footer * removed excludedRowRendererIds * Fixed typecheck * remove tests changes * Cleaned up unused selectors * Removed savedObjectId from tgrid state * fixed type check * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Resolved the comments * Fixed due to comments * Fixed type checks * Fixed tests * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * fixed merge issue * Move suricata-sid-db to lazy loaded modules * Fixed test * moved mitre helpers to async chunk * Fixed due to comments * Fixed tests * Renamed TableId.detectionsRulesDetailsPage -> TableId.alertsOnRuleDetailsPage TableId.detectionsPage -> TableId.alertsOnAlertsPage * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixed typecheck * Fixed test Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2b11ca723e
commit
ade016bad5
381 changed files with 5524 additions and 9651 deletions
|
@ -105,7 +105,7 @@ pageLoadAssetSize:
|
|||
screenshotting: 22870
|
||||
searchprofiler: 67080
|
||||
security: 65433
|
||||
securitySolution: 273763
|
||||
securitySolution: 66738
|
||||
sessionView: 77750
|
||||
share: 71239
|
||||
snapshotRestore: 79032
|
||||
|
|
|
@ -75,5 +75,5 @@ const requiredProperties: CellValueElementProps = {
|
|||
},
|
||||
isDraggable: false,
|
||||
linkValues: [],
|
||||
timelineId: '',
|
||||
scopeId: '',
|
||||
};
|
||||
|
|
|
@ -221,7 +221,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
|||
|
||||
const onStateChange = useCallback(
|
||||
(state: TGridState) => {
|
||||
const pickedState = pick(state.timelineById['standalone-t-grid'], [
|
||||
const pickedState = pick(state.tableById['standalone-t-grid'], [
|
||||
'columns',
|
||||
'sort',
|
||||
'selectedEventIds',
|
||||
|
@ -259,7 +259,6 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) {
|
|||
itemsPerPage,
|
||||
itemsPerPageOptions: [10, 25, 50],
|
||||
loadingText: translations.alertsTable.loadingTextLabel,
|
||||
footerText: translations.alertsTable.footerTextLabel,
|
||||
onStateChange,
|
||||
query: {
|
||||
query: kuery ?? '',
|
||||
|
|
|
@ -313,34 +313,36 @@ export type TimelineWithoutExternalRefs = Omit<SavedTimeline, 'dataViewId' | 'sa
|
|||
*/
|
||||
|
||||
export enum TimelineId {
|
||||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'timeline-test', // Reserved for testing purposes
|
||||
}
|
||||
|
||||
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.
|
||||
detectionsRulesDetailsPage = 'detections-rules-details-page',
|
||||
detectionsPage = 'detections-page',
|
||||
active = 'timeline-1',
|
||||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
alertsOnRuleDetailsPage = 'alerts-rules-details-page',
|
||||
alertsOnAlertsPage = 'alerts-page',
|
||||
test = 'table-test', // Reserved for testing purposes
|
||||
alternateTest = 'alternateTest',
|
||||
rulePreview = 'rule-preview',
|
||||
kubernetesPageSessions = 'kubernetes-page-sessions',
|
||||
}
|
||||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
runtimeTypes.literal(TimelineId.usersPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageEvents),
|
||||
runtimeTypes.literal(TimelineId.networkPageEvents),
|
||||
runtimeTypes.literal(TimelineId.hostsPageSessions),
|
||||
runtimeTypes.literal(TimelineId.detectionsRulesDetailsPage),
|
||||
runtimeTypes.literal(TimelineId.detectionsPage),
|
||||
runtimeTypes.literal(TimelineId.active),
|
||||
runtimeTypes.literal(TimelineId.test),
|
||||
runtimeTypes.literal(TimelineId.rulePreview),
|
||||
runtimeTypes.literal(TimelineId.kubernetesPageSessions),
|
||||
export 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 TimelineIdLiteral = runtimeTypes.TypeOf<typeof TimelineIdLiteralRt>;
|
||||
export type TableIdLiteral = runtimeTypes.TypeOf<typeof TableIdLiteralRt>;
|
||||
|
||||
export const TimelineSavedToReturnObjectRuntimeType = runtimeTypes.intersection([
|
||||
SavedTimelineRuntimeType,
|
||||
|
@ -528,7 +530,7 @@ export type TimelineExpandedDetail = {
|
|||
|
||||
export type ToggleDetailPanel = TimelineExpandedDetailType & {
|
||||
tabType?: TimelineTabs;
|
||||
timelineId: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export const pageInfoTimeline = runtimeTypes.type({
|
||||
|
|
|
@ -26,6 +26,7 @@ import { TimelineId } from '../../../../common/types/timeline';
|
|||
import { createStore } from '../../../common/store';
|
||||
import { kibanaObservable } from '@kbn/timelines-plugin/public/mock';
|
||||
import { sourcererPaths } from '../../../common/containers/sourcerer';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const actual = jest.requireActual('react-router-dom');
|
||||
|
@ -72,7 +73,13 @@ describe('global header', () => {
|
|||
},
|
||||
};
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
useVariationMock.mockReset();
|
||||
|
@ -169,7 +176,13 @@ describe('global header', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
const mockStore = createStore(mockstate, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const mockStore = createStore(
|
||||
mockstate,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({ pathname: sourcererPaths[2] });
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ import type { TimelineUrl } from '../../timelines/store/timeline/model';
|
|||
import { timelineDefaults } from '../../timelines/store/timeline/defaults';
|
||||
import { URL_PARAM_KEY } from '../../common/hooks/use_url_state';
|
||||
import { InputsModelId } from '../../common/store/inputs/constants';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
jest.mock('../../common/store/inputs/actions');
|
||||
|
||||
|
@ -298,7 +299,13 @@ describe('HomePage', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const mockStore = createStore(mockstate, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const mockStore = createStore(
|
||||
mockstate,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={mockStore}>
|
||||
|
@ -452,7 +459,13 @@ describe('HomePage', () => {
|
|||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const mockStore = createStore(mockstate, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const mockStore = createStore(
|
||||
mockstate,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
const TestComponent = () => (
|
||||
<TestProviders store={mockStore}>
|
||||
|
@ -509,7 +522,13 @@ describe('HomePage', () => {
|
|||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const mockStore = createStore(mockstate, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const mockStore = createStore(
|
||||
mockstate,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
const TestComponent = () => (
|
||||
<TestProviders store={mockStore}>
|
||||
|
@ -569,7 +588,13 @@ describe('HomePage', () => {
|
|||
|
||||
it('it removes empty timeline state from URL', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, {
|
||||
id: 'testSavedTimelineId',
|
||||
|
@ -596,7 +621,13 @@ describe('HomePage', () => {
|
|||
|
||||
it('it updates URL when timeline store changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const savedObjectId = 'testTimelineId';
|
||||
|
||||
mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, {
|
||||
|
|
|
@ -18,6 +18,8 @@ import type {
|
|||
import type { RouteProps } from 'react-router-dom';
|
||||
import type { AppMountParameters } from '@kbn/core/public';
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import type { TableState } from '@kbn/timelines-plugin/public';
|
||||
|
||||
import type { StartServices } from '../types';
|
||||
|
||||
/**
|
||||
|
@ -33,7 +35,6 @@ export interface RenderAppProps extends AppMountParameters {
|
|||
import type { State, SubPluginsInitReducer } from '../common/store';
|
||||
import type { Immutable } from '../../common/endpoint/types';
|
||||
import type { AppAction } from '../common/store/actions';
|
||||
import type { TimelineState } from '../timelines/store/timeline/types';
|
||||
|
||||
export { SecurityPageName } from '../../common/constants';
|
||||
|
||||
|
@ -47,7 +48,7 @@ export type SecuritySubPluginRoutes = RouteProps[];
|
|||
|
||||
export interface SecuritySubPlugin {
|
||||
routes: SecuritySubPluginRoutes;
|
||||
storageTimelines?: Pick<TimelineState, 'timelineById'>;
|
||||
storageDataTables?: Pick<TableState, 'tableById'>;
|
||||
}
|
||||
|
||||
export type SecuritySubPluginKeyStore =
|
||||
|
|
|
@ -34,7 +34,7 @@ const TimelineDetailsPanel = () => {
|
|||
entityType="events"
|
||||
isFlyoutView
|
||||
runtimeMappings={runtimeMappings}
|
||||
timelineId={TimelineId.casePage}
|
||||
scopeId={TimelineId.casePage}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -58,7 +58,7 @@ const CaseContainerComponent: React.FC = () => {
|
|||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
panelView: 'eventDetail',
|
||||
timelineId: TimelineId.casePage,
|
||||
id: TimelineId.casePage,
|
||||
params: {
|
||||
eventId: alertId,
|
||||
indexName: index,
|
||||
|
|
|
@ -19,6 +19,7 @@ import type { State } from '../../../../store';
|
|||
import { createStore } from '../../../../store';
|
||||
import * as i18n from './translations';
|
||||
import { useChartSettingsPopoverConfiguration } from '.';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const mockHandleClick = jest.fn();
|
||||
|
||||
|
@ -32,7 +33,13 @@ describe('useChartSettingsPopoverConfiguration', () => {
|
|||
|
||||
const state: State = mockGlobalState;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<TestProviders store={store}>{children}</TestProviders>
|
||||
);
|
||||
|
|
|
@ -168,7 +168,7 @@ export interface BarChartComponentProps {
|
|||
barChart: ChartSeriesData[] | null | undefined;
|
||||
configs?: ChartSeriesConfigs | undefined;
|
||||
stackByField?: string;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
visualizationActionsOptions?: VisualizationActionsProps;
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ export const BarChartComponent: React.FC<BarChartComponentProps> = ({
|
|||
barChart,
|
||||
configs,
|
||||
stackByField,
|
||||
timelineId,
|
||||
scopeId,
|
||||
visualizationActionsOptions,
|
||||
}) => {
|
||||
const { ref: measureRef, width, height } = useThrottledResizeObserver();
|
||||
|
@ -190,12 +190,12 @@ export const BarChartComponent: React.FC<BarChartComponentProps> = ({
|
|||
dataProviderId: escapeDataProviderId(
|
||||
`draggable-legend-item-${uuid.v4()}-${stackByField}-${d.key}`
|
||||
),
|
||||
timelineId,
|
||||
scopeId,
|
||||
field: stackByField,
|
||||
value: d.key,
|
||||
}))
|
||||
: NO_LEGEND_DATA,
|
||||
[barChart, stackByField, timelineId]
|
||||
[barChart, stackByField, scopeId]
|
||||
);
|
||||
|
||||
const yAxisTitle = get('yAxisTitle', configs);
|
||||
|
@ -243,7 +243,7 @@ export const BarChart = React.memo(
|
|||
BarChartComponent,
|
||||
(prevProps, nextProps) =>
|
||||
prevProps.stackByField === nextProps.stackByField &&
|
||||
prevProps.timelineId === nextProps.timelineId &&
|
||||
prevProps.scopeId === nextProps.scopeId &&
|
||||
deepEqual(prevProps.configs, nextProps.configs) &&
|
||||
deepEqual(prevProps.barChart, nextProps.barChart)
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ export interface LegendItem {
|
|||
dataProviderId: string;
|
||||
render?: (fieldValuePair?: { field: string; value: string | number }) => React.ReactNode;
|
||||
field: string;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
value: string | number;
|
||||
count?: number;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ const DraggableLegendItemComponent: React.FC<{
|
|||
legendItem: LegendItem;
|
||||
}> = ({ legendItem }) => {
|
||||
const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT);
|
||||
const { color, count, dataProviderId, field, timelineId, value } = legendItem;
|
||||
const { color, count, dataProviderId, field, scopeId, value } = legendItem;
|
||||
|
||||
return (
|
||||
<EuiText size="xs">
|
||||
|
@ -72,7 +72,7 @@ const DraggableLegendItemComponent: React.FC<{
|
|||
hideTopN={true}
|
||||
id={dataProviderId}
|
||||
isDraggable={false}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
value={value}
|
||||
>
|
||||
{legendItem.render == null ? (
|
||||
|
|
|
@ -11,7 +11,7 @@ import React from 'react';
|
|||
import type { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd';
|
||||
|
||||
import '../../mock/match_media';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { TableId, TimelineId } from '../../../../common/types';
|
||||
import { mockBrowserFields } from '../../containers/source/mock';
|
||||
import { TestProviders } from '../../mock';
|
||||
import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers';
|
||||
|
@ -35,25 +35,22 @@ jest.mock('@elastic/eui', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const timelineIdsWithHoverActions = [
|
||||
const scopeIdsWithHoverActions = [
|
||||
undefined,
|
||||
TimelineId.active,
|
||||
TimelineId.alternateTest,
|
||||
TableId.alternateTest,
|
||||
TimelineId.casePage,
|
||||
TimelineId.detectionsPage,
|
||||
TimelineId.detectionsRulesDetailsPage,
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageSessions,
|
||||
TimelineId.kubernetesPageSessions,
|
||||
TimelineId.networkPageEvents,
|
||||
TableId.alertsOnAlertsPage,
|
||||
TableId.alertsOnRuleDetailsPage,
|
||||
TableId.hostsPageEvents,
|
||||
TableId.hostsPageSessions,
|
||||
TableId.kubernetesPageSessions,
|
||||
TableId.networkPageEvents,
|
||||
TimelineId.test,
|
||||
TimelineId.usersPageEvents,
|
||||
TableId.usersPageEvents,
|
||||
];
|
||||
|
||||
const timelineIdsNoHoverActions = [
|
||||
TimelineId.rulePreview,
|
||||
ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID,
|
||||
];
|
||||
const scopeIdsNoHoverActions = [TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID];
|
||||
|
||||
describe('DraggableWrapper', () => {
|
||||
const dataProvider = mockDataProviders[0];
|
||||
|
@ -141,8 +138,8 @@ describe('DraggableWrapper', () => {
|
|||
});
|
||||
});
|
||||
|
||||
timelineIdsWithHoverActions.forEach((timelineId) => {
|
||||
test(`it renders hover actions (by default) when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => {
|
||||
scopeIdsWithHoverActions.forEach((scopeId) => {
|
||||
test(`it renders hover actions (by default) when 'isDraggable' is false and timelineId is '${scopeId}'`, async () => {
|
||||
const isDraggable = false;
|
||||
|
||||
const { container } = render(
|
||||
|
@ -152,7 +149,7 @@ describe('DraggableWrapper', () => {
|
|||
dataProvider={dataProvider}
|
||||
isDraggable={isDraggable}
|
||||
render={() => message}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
</DragDropContextWrapper>
|
||||
</TestProviders>
|
||||
|
@ -166,8 +163,8 @@ describe('DraggableWrapper', () => {
|
|||
});
|
||||
});
|
||||
|
||||
timelineIdsNoHoverActions.forEach((timelineId) => {
|
||||
test(`it does NOT render hover actions when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => {
|
||||
scopeIdsNoHoverActions.forEach((scopeId) => {
|
||||
test(`it does NOT render hover actions when 'isDraggable' is false and timelineId is '${scopeId}'`, async () => {
|
||||
const isDraggable = false;
|
||||
|
||||
const { container } = render(
|
||||
|
@ -177,7 +174,7 @@ describe('DraggableWrapper', () => {
|
|||
dataProvider={dataProvider}
|
||||
isDraggable={isDraggable}
|
||||
render={() => message}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
</DragDropContextWrapper>
|
||||
</TestProviders>
|
||||
|
@ -281,15 +278,15 @@ describe('ConditionalPortal', () => {
|
|||
});
|
||||
|
||||
describe('disableHoverActions', () => {
|
||||
timelineIdsNoHoverActions.forEach((timelineId) =>
|
||||
test(`it returns true when timelineId is ${timelineId}`, () => {
|
||||
expect(disableHoverActions(timelineId)).toBe(true);
|
||||
scopeIdsNoHoverActions.forEach((scopeId) =>
|
||||
test(`it returns true when timelineId is ${scopeId}`, () => {
|
||||
expect(disableHoverActions(scopeId)).toBe(true);
|
||||
})
|
||||
);
|
||||
|
||||
timelineIdsWithHoverActions.forEach((timelineId) =>
|
||||
test(`it returns false when timelineId is ${timelineId}`, () => {
|
||||
expect(disableHoverActions(timelineId)).toBe(false);
|
||||
scopeIdsWithHoverActions.forEach((scopeId) =>
|
||||
test(`it returns false when timelineId is ${scopeId}`, () => {
|
||||
expect(disableHoverActions(scopeId)).toBe(false);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Draggable, Droppable } from 'react-beautiful-dnd';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { TableId } from '../../../../common/types';
|
||||
import { dragAndDropActions } from '../../store/drag_and_drop';
|
||||
import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
|
||||
import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
|
||||
|
@ -104,13 +104,13 @@ interface Props {
|
|||
render: RenderFunctionProp;
|
||||
isAggregatable?: boolean;
|
||||
fieldType?: string;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
truncate?: boolean;
|
||||
onFilterAdded?: () => void;
|
||||
}
|
||||
|
||||
export const disableHoverActions = (timelineId: string | undefined): boolean =>
|
||||
[TimelineId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? '');
|
||||
[TableId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? '');
|
||||
|
||||
/**
|
||||
* Wraps a draggable component to handle registration / unregistration of the
|
||||
|
@ -138,7 +138,7 @@ const DraggableOnWrapperComponent: React.FC<Props> = ({
|
|||
render,
|
||||
fieldType = '',
|
||||
isAggregatable = false,
|
||||
timelineId,
|
||||
scopeId,
|
||||
truncate,
|
||||
}) => {
|
||||
const [providerRegistered, setProviderRegistered] = useState(false);
|
||||
|
@ -163,7 +163,7 @@ const DraggableOnWrapperComponent: React.FC<Props> = ({
|
|||
render,
|
||||
fieldType,
|
||||
isAggregatable,
|
||||
timelineId,
|
||||
scopeId,
|
||||
truncate,
|
||||
});
|
||||
|
||||
|
@ -324,7 +324,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
render,
|
||||
isAggregatable = false,
|
||||
fieldType = '',
|
||||
timelineId,
|
||||
scopeId,
|
||||
truncate,
|
||||
}) => {
|
||||
const {
|
||||
|
@ -342,7 +342,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
fieldType,
|
||||
onFilterAdded,
|
||||
render,
|
||||
timelineId,
|
||||
scopeId,
|
||||
truncate,
|
||||
});
|
||||
const renderContent = useCallback(
|
||||
|
@ -374,7 +374,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
<WithHoverActions
|
||||
alwaysShow={showTopN || hoverActionsOwnFocus}
|
||||
closePopOverTrigger={closePopOverTrigger}
|
||||
hoverContent={disableHoverActions(timelineId) ? undefined : hoverContent}
|
||||
hoverContent={disableHoverActions(scopeId) ? undefined : hoverContent}
|
||||
onCloseRequested={onCloseRequested}
|
||||
render={renderContent}
|
||||
/>
|
||||
|
@ -388,7 +388,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
fieldType={fieldType}
|
||||
isAggregatable={isAggregatable}
|
||||
render={render}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
truncate={truncate}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -28,7 +28,7 @@ export interface DefaultDraggableType {
|
|||
name?: string | null;
|
||||
queryValue?: string | null;
|
||||
children?: React.ReactNode;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
tooltipContent?: React.ReactNode;
|
||||
tooltipPosition?: ToolTipPositions;
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ export const DefaultDraggable = React.memo<DefaultDraggableType>(
|
|||
value,
|
||||
name,
|
||||
children,
|
||||
timelineId,
|
||||
scopeId,
|
||||
tooltipContent,
|
||||
tooltipPosition,
|
||||
queryValue,
|
||||
|
@ -158,7 +158,7 @@ export const DefaultDraggable = React.memo<DefaultDraggableType>(
|
|||
hideTopN={hideTopN}
|
||||
isDraggable={isDraggable}
|
||||
render={renderCallback}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ const DraggableBadgeComponent: React.FC<BadgeDraggableType> = ({
|
|||
name,
|
||||
color = 'hollow',
|
||||
children,
|
||||
timelineId,
|
||||
scopeId,
|
||||
tooltipContent,
|
||||
queryValue,
|
||||
}) =>
|
||||
|
@ -220,7 +220,7 @@ const DraggableBadgeComponent: React.FC<BadgeDraggableType> = ({
|
|||
field={field}
|
||||
name={name}
|
||||
value={value}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
tooltipContent={tooltipContent}
|
||||
queryValue={queryValue}
|
||||
>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React, { memo, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { TimelineId } from '@kbn/timelines-plugin/common';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import type { AppLocation } from '../../../../common/endpoint/types';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
|
||||
|
|
|
@ -19,14 +19,27 @@ import { createStore } from '../../store/store';
|
|||
|
||||
import { ErrorToastDispatcher } from '.';
|
||||
import type { State } from '../../store/types';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
describe('Error Toast Dispatcher', () => {
|
||||
const state: State = mockGlobalState;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
let store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
|
|
|
@ -30,7 +30,7 @@ const props = {
|
|||
data: mockAlertDetailsData as TimelineEventsDetailsItem[],
|
||||
browserFields: mockBrowserFields,
|
||||
eventId: '5d1d53da502f56aacc14c3cb5c669363d102b31f99822e5d369d4804ed370a31',
|
||||
timelineId: 'detections-page',
|
||||
scopeId: 'alerts-page',
|
||||
title: '',
|
||||
goToTable: jest.fn(),
|
||||
};
|
||||
|
@ -84,7 +84,7 @@ describe('AlertSummaryView', () => {
|
|||
await act(async () => {
|
||||
const { queryAllByTestId } = render(
|
||||
<TestProviders>
|
||||
<AlertSummaryView {...props} timelineId={TimelineId.active} />
|
||||
<AlertSummaryView {...props} scopeId={TimelineId.active} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0);
|
||||
|
|
|
@ -19,14 +19,14 @@ const AlertSummaryViewComponent: React.FC<{
|
|||
data: TimelineEventsDetailsItem[];
|
||||
eventId: string;
|
||||
isDraggable?: boolean;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
title: string;
|
||||
goToTable: () => void;
|
||||
isReadOnly?: boolean;
|
||||
}> = ({ browserFields, data, eventId, isDraggable, timelineId, title, goToTable, isReadOnly }) => {
|
||||
}> = ({ browserFields, data, eventId, isDraggable, scopeId, title, goToTable, isReadOnly }) => {
|
||||
const summaryRows = useMemo(
|
||||
() => getSummaryRows({ browserFields, data, eventId, isDraggable, timelineId, isReadOnly }),
|
||||
[browserFields, data, eventId, isDraggable, timelineId, isReadOnly]
|
||||
() => getSummaryRows({ browserFields, data, eventId, isDraggable, scopeId, isReadOnly }),
|
||||
[browserFields, data, eventId, isDraggable, scopeId, isReadOnly]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('getColumns', () => {
|
|||
eventId: 'some-event',
|
||||
getLinkValue: jest.fn(),
|
||||
onUpdateColumns: jest.fn(),
|
||||
timelineId: 'some-timeline',
|
||||
scopeId: 'some-timeline',
|
||||
toggleColumn: jest.fn(),
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ export const getColumns = ({
|
|||
eventId,
|
||||
onUpdateColumns,
|
||||
contextId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
toggleColumn,
|
||||
getLinkValue,
|
||||
isDraggable,
|
||||
|
@ -55,7 +55,7 @@ export const getColumns = ({
|
|||
eventId: string;
|
||||
onUpdateColumns: OnUpdateColumns;
|
||||
contextId: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
toggleColumn: (column: ColumnHeaderOptions) => void;
|
||||
getLinkValue: (field: string) => string | null;
|
||||
isDraggable?: boolean;
|
||||
|
@ -90,7 +90,7 @@ export const getColumns = ({
|
|||
fieldFromBrowserField={fieldFromBrowserField}
|
||||
getLinkValue={getLinkValue}
|
||||
toggleColumn={toggleColumn}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
values={values}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -30,7 +30,7 @@ export interface ThreatSummaryDescription {
|
|||
eventId: string;
|
||||
index: number;
|
||||
feedName: string | undefined;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
value: string | undefined;
|
||||
isDraggable?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
|
@ -63,19 +63,19 @@ const EnrichmentDescription: React.FC<ThreatSummaryDescription> = ({
|
|||
eventId,
|
||||
index,
|
||||
feedName,
|
||||
timelineId,
|
||||
scopeId,
|
||||
value,
|
||||
isDraggable,
|
||||
isReadOnly,
|
||||
}) => {
|
||||
if (!data || !value) return null;
|
||||
const key = `alert-details-value-formatted-field-value-${timelineId}-${eventId}-${data.field}-${value}-${index}-${feedName}`;
|
||||
const key = `alert-details-value-formatted-field-value-${scopeId}-${eventId}-${data.field}-${value}-${index}-${feedName}`;
|
||||
return (
|
||||
<StyledEuiFlexGroup key={key} direction="row" gutterSize="xs" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<div>
|
||||
<FormattedFieldValue
|
||||
contextId={timelineId}
|
||||
contextId={scopeId}
|
||||
eventId={key}
|
||||
fieldFormat={data.format}
|
||||
fieldName={data.field}
|
||||
|
@ -97,10 +97,10 @@ const EnrichmentDescription: React.FC<ThreatSummaryDescription> = ({
|
|||
{value && !isReadOnly && (
|
||||
<ActionCell
|
||||
data={data}
|
||||
contextId={timelineId}
|
||||
contextId={scopeId}
|
||||
eventId={key}
|
||||
fieldFromBrowserField={browserField}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
values={[value]}
|
||||
applyWidthAndPadding={false}
|
||||
/>
|
||||
|
@ -114,11 +114,11 @@ const EnrichmentSummaryComponent: React.FC<{
|
|||
browserFields: BrowserFields;
|
||||
data: TimelineEventsDetailsItem[];
|
||||
enrichments: CtiEnrichment[];
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
eventId: string;
|
||||
isDraggable?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
}> = ({ browserFields, data, enrichments, timelineId, eventId, isDraggable, isReadOnly }) => {
|
||||
}> = ({ browserFields, data, enrichments, scopeId, eventId, isDraggable, isReadOnly }) => {
|
||||
const parsedEnrichments = enrichments.map((enrichment, index) => {
|
||||
const { field, type, feedName, value } = getEnrichmentIdentifiers(enrichment);
|
||||
const eventData = data.find((item) => item.field === field);
|
||||
|
@ -166,7 +166,7 @@ const EnrichmentSummaryComponent: React.FC<{
|
|||
eventId={eventId}
|
||||
index={index}
|
||||
feedName={feedName}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
value={value}
|
||||
data={fieldsData}
|
||||
browserField={browserField}
|
||||
|
@ -197,7 +197,7 @@ const EnrichmentSummaryComponent: React.FC<{
|
|||
eventId={eventId}
|
||||
index={index}
|
||||
feedName={feedName}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
value={value}
|
||||
data={fieldsData}
|
||||
browserField={browserField}
|
||||
|
|
|
@ -37,7 +37,7 @@ const EMPTY_RISK_SCORE = {
|
|||
|
||||
describe('ThreatSummaryView', () => {
|
||||
const eventId = '5d1d53da502f56aacc14c3cb5c669363d102b31f99822e5d369d4804ed370a31';
|
||||
const timelineId = 'detections-page';
|
||||
const scopeId = 'alerts-page';
|
||||
const data = mockAlertDetailsData as TimelineEventsDetailsItem[];
|
||||
const browserFields = mockBrowserFields;
|
||||
|
||||
|
@ -54,7 +54,7 @@ describe('ThreatSummaryView', () => {
|
|||
browserFields={browserFields}
|
||||
enrichments={enrichments}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
hostRisk={EMPTY_RISK_SCORE}
|
||||
userRisk={EMPTY_RISK_SCORE}
|
||||
/>
|
||||
|
|
|
@ -126,7 +126,7 @@ const ThreatSummaryViewComponent: React.FC<{
|
|||
data: TimelineEventsDetailsItem[];
|
||||
enrichments: CtiEnrichment[];
|
||||
eventId: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
hostRisk: HostRisk;
|
||||
userRisk: UserRisk;
|
||||
isDraggable?: boolean;
|
||||
|
@ -136,7 +136,7 @@ const ThreatSummaryViewComponent: React.FC<{
|
|||
data,
|
||||
enrichments,
|
||||
eventId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
hostRisk,
|
||||
userRisk,
|
||||
isDraggable,
|
||||
|
@ -180,7 +180,7 @@ const ThreatSummaryViewComponent: React.FC<{
|
|||
browserFields={browserFields}
|
||||
data={data}
|
||||
enrichments={enrichments}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
eventId={eventId}
|
||||
isDraggable={isDraggable}
|
||||
isReadOnly={isReadOnly}
|
||||
|
|
|
@ -72,7 +72,7 @@ describe('EventDetails', () => {
|
|||
onEventViewSelected: jest.fn(),
|
||||
onThreatViewSelected: jest.fn(),
|
||||
timelineTabType: TimelineTabs.query,
|
||||
timelineId: 'test',
|
||||
scopeId: 'table-test',
|
||||
eventView: EventsViewType.summaryView,
|
||||
hostRisk: { fields: [], loading: true },
|
||||
indexName: 'test',
|
||||
|
|
|
@ -77,7 +77,7 @@ interface Props {
|
|||
isDraggable?: boolean;
|
||||
rawEventData: object | undefined;
|
||||
timelineTabType: TimelineTabs | 'flyout';
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
handleOnEventClosed: () => void;
|
||||
isReadOnly?: boolean;
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
isAlert,
|
||||
isDraggable,
|
||||
rawEventData,
|
||||
timelineId,
|
||||
scopeId,
|
||||
timelineTabType,
|
||||
handleOnEventClosed,
|
||||
isReadOnly,
|
||||
|
@ -197,11 +197,11 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
<EuiSpacer size="m" />
|
||||
<Overview
|
||||
browserFields={browserFields}
|
||||
contextId={timelineId}
|
||||
contextId={scopeId}
|
||||
data={data}
|
||||
eventId={id}
|
||||
indexName={indexName}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
handleOnEventClosed={handleOnEventClosed}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
|
@ -214,7 +214,7 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
contextId: EVENT_DETAILS_CONTEXT_ID,
|
||||
data: detailsEcsData,
|
||||
isDraggable: isDraggable ?? false,
|
||||
timelineId,
|
||||
scopeId,
|
||||
})}
|
||||
</RendererContainer>
|
||||
</div>
|
||||
|
@ -227,7 +227,7 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
eventId: id,
|
||||
browserFields,
|
||||
isDraggable,
|
||||
timelineId,
|
||||
scopeId,
|
||||
title: i18n.HIGHLIGHTED_FIELDS,
|
||||
isReadOnly,
|
||||
}}
|
||||
|
@ -239,7 +239,7 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
browserFields={browserFields}
|
||||
eventId={id}
|
||||
data={data}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
|
||||
|
@ -251,7 +251,7 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
browserFields={browserFields}
|
||||
data={data}
|
||||
eventId={id}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
enrichments={allEnrichments}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
|
@ -280,11 +280,11 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
indexName,
|
||||
isAlert,
|
||||
isDraggable,
|
||||
scopeId,
|
||||
isEnrichmentsLoading,
|
||||
showThreatSummary,
|
||||
isReadOnly,
|
||||
renderer,
|
||||
timelineId,
|
||||
userRisk,
|
||||
]
|
||||
);
|
||||
|
@ -359,14 +359,14 @@ const EventDetailsComponent: React.FC<Props> = ({
|
|||
data={data}
|
||||
eventId={id}
|
||||
isDraggable={isDraggable}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
timelineTabType={timelineTabType}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
}),
|
||||
[browserFields, data, id, isDraggable, timelineId, timelineTabType, isReadOnly]
|
||||
[browserFields, data, id, isDraggable, scopeId, timelineTabType, isReadOnly]
|
||||
);
|
||||
|
||||
const jsonTab = useMemo(
|
||||
|
|
|
@ -50,7 +50,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -69,7 +69,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -91,7 +91,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -107,7 +107,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -123,7 +123,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -139,7 +139,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
isReadOnly
|
||||
/>
|
||||
|
@ -158,7 +158,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -176,7 +176,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -194,7 +194,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -214,7 +214,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -239,7 +239,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -254,7 +254,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -279,7 +279,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -298,7 +298,7 @@ describe('EventFieldsBrowser', () => {
|
|||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={mockDetailItemDataId}
|
||||
timelineId="test"
|
||||
scopeId="timeline-test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -19,8 +19,11 @@ import {
|
|||
onKeyDownFocusHandler,
|
||||
} from '@kbn/timelines-plugin/public';
|
||||
|
||||
import { getScopedActions, isInTableScope, isTimelineScope } from '../../../helpers';
|
||||
import { tableDefaults } from '../../store/data_table/defaults';
|
||||
import { dataTableSelectors } from '../../store/data_table';
|
||||
import { ADD_TIMELINE_BUTTON_CLASS_NAME } from '../../../timelines/components/flyout/add_timeline_button';
|
||||
import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import type { BrowserFields } from '../../containers/source';
|
||||
import { getAllFieldsByName } from '../../containers/source';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline';
|
||||
|
@ -36,7 +39,7 @@ interface Props {
|
|||
data: TimelineEventsDetailsItem[];
|
||||
eventId: string;
|
||||
isDraggable?: boolean;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
timelineTabType: TimelineTabs | 'flyout';
|
||||
isReadOnly?: boolean;
|
||||
}
|
||||
|
@ -166,10 +169,22 @@ const useFieldBrowserPagination = () => {
|
|||
|
||||
/** Renders a table view or JSON view of the `ECS` `data` */
|
||||
export const EventFieldsBrowser = React.memo<Props>(
|
||||
({ browserFields, data, eventId, isDraggable, timelineTabType, timelineId, isReadOnly }) => {
|
||||
({ browserFields, data, eventId, isDraggable, timelineTabType, scopeId, isReadOnly }) => {
|
||||
const containerElement = useRef<HTMLDivElement | null>(null);
|
||||
const dispatch = useDispatch();
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
const getScope = useMemo(() => {
|
||||
if (isTimelineScope(scopeId)) {
|
||||
return timelineSelectors.getTimelineByIdSelector();
|
||||
} else if (isInTableScope(scopeId)) {
|
||||
return dataTableSelectors.getTableByIdSelector();
|
||||
}
|
||||
}, [scopeId]);
|
||||
const defaults = isTimelineScope(scopeId) ? timelineDefaults : tableDefaults;
|
||||
const columnHeaders = useDeepEqualSelector((state) => {
|
||||
const { columns } = (getScope && getScope(state, scopeId)) ?? defaults;
|
||||
return getColumnHeaders(columns, browserFields);
|
||||
});
|
||||
|
||||
const fieldsByName = useMemo(() => getAllFieldsByName(browserFields), [browserFields]);
|
||||
const items = useMemo(
|
||||
() =>
|
||||
|
@ -182,11 +197,6 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
[data, fieldsByName]
|
||||
);
|
||||
|
||||
const columnHeaders = useDeepEqualSelector((state) => {
|
||||
const { columns } = getTimeline(state, timelineId) ?? timelineDefaults;
|
||||
return getColumnHeaders(columns, browserFields);
|
||||
});
|
||||
|
||||
const getLinkValue = useCallback(
|
||||
(field: string) => {
|
||||
const linkField = (columnHeaders.find((col) => col.id === field) ?? {}).linkField;
|
||||
|
@ -199,27 +209,30 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
},
|
||||
[data, columnHeaders]
|
||||
);
|
||||
|
||||
const scopedActions = getScopedActions(scopeId);
|
||||
const toggleColumn = useCallback(
|
||||
(column: ColumnHeaderOptions) => {
|
||||
if (!scopedActions) {
|
||||
return;
|
||||
}
|
||||
if (columnHeaders.some((c) => c.id === column.id)) {
|
||||
dispatch(
|
||||
timelineActions.removeColumn({
|
||||
scopedActions.removeColumn({
|
||||
columnId: column.id,
|
||||
id: timelineId,
|
||||
id: scopeId,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
timelineActions.upsertColumn({
|
||||
scopedActions.upsertColumn({
|
||||
column,
|
||||
id: timelineId,
|
||||
id: scopeId,
|
||||
index: 1,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[columnHeaders, dispatch, timelineId]
|
||||
[columnHeaders, dispatch, scopeId, scopedActions]
|
||||
);
|
||||
|
||||
const onSetRowProps = useCallback(({ ariaRowindex, field }: TimelineEventsDetailsItem) => {
|
||||
|
@ -232,8 +245,12 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
}, []);
|
||||
|
||||
const onUpdateColumns = useCallback(
|
||||
(columns) => dispatch(timelineActions.updateColumns({ id: timelineId, columns })),
|
||||
[dispatch, timelineId]
|
||||
(columns) => {
|
||||
if (scopedActions) {
|
||||
dispatch(scopedActions.updateColumns({ id: scopeId, columns }));
|
||||
}
|
||||
},
|
||||
[dispatch, scopeId, scopedActions]
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
|
@ -243,8 +260,8 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
columnHeaders,
|
||||
eventId,
|
||||
onUpdateColumns,
|
||||
contextId: `event-fields-browser-for-${timelineId}-${timelineTabType}`,
|
||||
timelineId,
|
||||
contextId: `event-fields-browser-for-${scopeId}-${timelineTabType}`,
|
||||
scopeId,
|
||||
toggleColumn,
|
||||
getLinkValue,
|
||||
isDraggable,
|
||||
|
@ -255,7 +272,7 @@ export const EventFieldsBrowser = React.memo<Props>(
|
|||
columnHeaders,
|
||||
eventId,
|
||||
onUpdateColumns,
|
||||
timelineId,
|
||||
scopeId,
|
||||
timelineTabType,
|
||||
toggleColumn,
|
||||
getLinkValue,
|
||||
|
|
|
@ -237,14 +237,14 @@ function getEventCategoriesFromData(data: TimelineEventsDetailsItem[]): EventCat
|
|||
export const getSummaryRows = ({
|
||||
data,
|
||||
browserFields,
|
||||
timelineId,
|
||||
scopeId,
|
||||
eventId,
|
||||
isDraggable = false,
|
||||
isReadOnly = false,
|
||||
}: {
|
||||
data: TimelineEventsDetailsItem[];
|
||||
browserFields: BrowserFields;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
eventId: string;
|
||||
isDraggable?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
|
@ -288,8 +288,8 @@ export const getSummaryRows = ({
|
|||
...getEnrichedFieldInfo({
|
||||
item,
|
||||
linkValueField: linkValueField || undefined,
|
||||
contextId: timelineId,
|
||||
timelineId,
|
||||
contextId: scopeId,
|
||||
scopeId,
|
||||
browserFields,
|
||||
eventId,
|
||||
field,
|
||||
|
|
|
@ -134,14 +134,14 @@ export function getEnrichedFieldInfo({
|
|||
field,
|
||||
item,
|
||||
linkValueField,
|
||||
timelineId,
|
||||
scopeId,
|
||||
}: {
|
||||
browserFields: BrowserFields;
|
||||
contextId: string;
|
||||
item: TimelineEventsDetailsItem;
|
||||
eventId: string;
|
||||
field?: EventSummaryField;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
linkValueField?: TimelineEventsDetailsItem;
|
||||
}): EnrichedFieldInfo {
|
||||
const fieldInfo = {
|
||||
|
@ -149,7 +149,7 @@ export function getEnrichedFieldInfo({
|
|||
eventId,
|
||||
fieldType: 'string',
|
||||
linkValue: undefined,
|
||||
timelineId,
|
||||
scopeId,
|
||||
};
|
||||
const linkValue = getOr(null, 'originalValue.0', linkValueField);
|
||||
const category = item.category ?? '';
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('Insights', () => {
|
|||
it('does not render when there is no content to show', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights browserFields={{}} eventId="test" data={[]} timelineId="" />
|
||||
<Insights browserFields={{}} eventId="test" data={[]} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -120,7 +120,7 @@ describe('Insights', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights browserFields={{}} eventId="test" data={[]} timelineId="" />
|
||||
<Insights browserFields={{}} eventId="test" data={[]} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -140,7 +140,7 @@ describe('Insights', () => {
|
|||
it('should show insights for related alerts by process ancestry', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights browserFields={{}} eventId="test" data={data} timelineId="" />
|
||||
<Insights browserFields={{}} eventId="test" data={data} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -154,12 +154,7 @@ describe('Insights', () => {
|
|||
it('should not show the related alerts by process ancestry insights module', () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights
|
||||
browserFields={{}}
|
||||
eventId="test"
|
||||
data={dataWithoutAgentType}
|
||||
timelineId=""
|
||||
/>
|
||||
<Insights browserFields={{}} eventId="test" data={dataWithoutAgentType} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -174,7 +169,7 @@ describe('Insights', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights browserFields={{}} eventId="test" data={data} timelineId="" />
|
||||
<Insights browserFields={{}} eventId="test" data={data} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -192,7 +187,7 @@ describe('Insights', () => {
|
|||
|
||||
render(
|
||||
<TestProviders>
|
||||
<Insights browserFields={{}} eventId="test" data={data} timelineId="" />
|
||||
<Insights browserFields={{}} eventId="test" data={data} scopeId="" />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ interface Props {
|
|||
browserFields: BrowserFields;
|
||||
eventId: string;
|
||||
data: TimelineEventsDetailsItem[];
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
isReadOnly?: boolean;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ interface Props {
|
|||
* Displays several key insights for the associated alert.
|
||||
*/
|
||||
export const Insights = React.memo<Props>(
|
||||
({ browserFields, eventId, data, isReadOnly, timelineId }) => {
|
||||
({ browserFields, eventId, data, isReadOnly, scopeId }) => {
|
||||
const isRelatedAlertsByProcessAncestryEnabled = useIsExperimentalFeatureEnabled(
|
||||
'insightsRelatedAlertsByProcessAncestry'
|
||||
);
|
||||
|
@ -113,7 +113,7 @@ export const Insights = React.memo<Props>(
|
|||
browserFields={browserFields}
|
||||
data={sourceEventField}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
@ -124,7 +124,7 @@ export const Insights = React.memo<Props>(
|
|||
browserFields={browserFields}
|
||||
data={processSessionField}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
@ -137,7 +137,7 @@ export const Insights = React.memo<Props>(
|
|||
originalDocumentId={originalDocumentId}
|
||||
index={originalDocumentIndex}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : (
|
||||
|
|
|
@ -43,6 +43,8 @@ const props = {
|
|||
values: ['original'],
|
||||
isObjectArray: false,
|
||||
},
|
||||
scopeId: 'table-test',
|
||||
isActiveTimelines: false,
|
||||
};
|
||||
describe('RelatedAlertsByProcessAncestry', () => {
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -9,8 +9,8 @@ import React, { useMemo, useCallback, useEffect, useState } from 'react';
|
|||
import { EuiBetaBadge, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import { isActiveTimeline } from '../../../../helpers';
|
||||
import type { DataProvider } from '../../../../../common/types';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline';
|
||||
import { getDataProvider } from '../table/use_action_cell_data_provider';
|
||||
import { useAlertPrevalenceFromProcessTree } from '../../../containers/alerts/use_alert_prevalence_from_process_tree';
|
||||
|
@ -32,7 +32,7 @@ interface Props {
|
|||
eventId: string;
|
||||
index: TimelineEventsDetailsItem;
|
||||
originalDocumentId: TimelineEventsDetailsItem;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
}
|
||||
|
||||
interface Cache {
|
||||
|
@ -70,7 +70,7 @@ const dataProviderLimit = 5;
|
|||
* state inside the component rather than to add it to Redux.
|
||||
*/
|
||||
export const RelatedAlertsByProcessAncestry = React.memo<Props>(
|
||||
({ data, originalDocumentId, index, eventId, timelineId }) => {
|
||||
({ data, originalDocumentId, index, eventId, scopeId }) => {
|
||||
const [showContent, setShowContent] = useState(false);
|
||||
const [cache, setCache] = useState<Partial<Cache>>({});
|
||||
|
||||
|
@ -85,7 +85,7 @@ export const RelatedAlertsByProcessAncestry = React.memo<Props>(
|
|||
return (
|
||||
<ActualRelatedAlertsByProcessAncestry
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
alertIds={cache.alertIds}
|
||||
/>
|
||||
);
|
||||
|
@ -96,11 +96,11 @@ export const RelatedAlertsByProcessAncestry = React.memo<Props>(
|
|||
index={index}
|
||||
originalDocumentId={originalDocumentId}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
isActiveTimelines={isActiveTimeline(scopeId ?? '')}
|
||||
onCacheLoad={setCache}
|
||||
/>
|
||||
);
|
||||
}, [showContent, cache, data, eventId, timelineId, index, originalDocumentId]);
|
||||
}, [showContent, cache.alertIds, data, index, originalDocumentId, eventId, scopeId]);
|
||||
|
||||
const betaBadge = useMemo(() => <EuiBetaBadge size="s" label={BETA} color="subdued" />, []);
|
||||
|
||||
|
@ -132,9 +132,9 @@ const FetchAndNotifyCachedAlertsByProcessAncestry: React.FC<{
|
|||
eventId: string;
|
||||
index: TimelineEventsDetailsItem;
|
||||
originalDocumentId: TimelineEventsDetailsItem;
|
||||
timelineId?: string;
|
||||
isActiveTimelines: boolean;
|
||||
onCacheLoad: (cache: Cache) => void;
|
||||
}> = ({ data, originalDocumentId, index, timelineId, onCacheLoad, eventId }) => {
|
||||
}> = ({ data, originalDocumentId, index, isActiveTimelines, onCacheLoad, eventId }) => {
|
||||
const { values: wrappedProcessEntityId } = data;
|
||||
const { values: indices } = index;
|
||||
const { values: wrappedDocumentId } = originalDocumentId;
|
||||
|
@ -142,7 +142,7 @@ const FetchAndNotifyCachedAlertsByProcessAncestry: React.FC<{
|
|||
const processEntityId = Array.isArray(wrappedProcessEntityId) ? wrappedProcessEntityId[0] : '';
|
||||
const { loading, error, alertIds } = useAlertPrevalenceFromProcessTree({
|
||||
processEntityId,
|
||||
timelineId: timelineId ?? TimelineId.active,
|
||||
isActiveTimeline: isActiveTimelines,
|
||||
documentId,
|
||||
indices: indices ?? [],
|
||||
});
|
||||
|
@ -173,8 +173,8 @@ FetchAndNotifyCachedAlertsByProcessAncestry.displayName =
|
|||
const ActualRelatedAlertsByProcessAncestry: React.FC<{
|
||||
alertIds: string[];
|
||||
eventId: string;
|
||||
timelineId?: string;
|
||||
}> = ({ alertIds, eventId, timelineId }) => {
|
||||
scopeId?: string;
|
||||
}> = ({ alertIds, eventId, scopeId }) => {
|
||||
const shouldUseFilters = alertIds && alertIds.length && alertIds.length >= dataProviderLimit;
|
||||
const dataProviders = useMemo(() => {
|
||||
if (alertIds && alertIds.length) {
|
||||
|
@ -182,14 +182,14 @@ const ActualRelatedAlertsByProcessAncestry: React.FC<{
|
|||
return null;
|
||||
} else {
|
||||
return alertIds.reduce<DataProvider[]>((result, alertId, index) => {
|
||||
const id = `${timelineId}-${eventId}-event.id-${index}-${alertId}`;
|
||||
const id = `${scopeId}-${eventId}-event.id-${index}-${alertId}`;
|
||||
result.push(getDataProvider('_id', id, alertId));
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [alertIds, eventId, timelineId, shouldUseFilters]);
|
||||
}, [alertIds, shouldUseFilters, scopeId, eventId]);
|
||||
|
||||
const filters: Filter[] | null = useMemo(() => {
|
||||
if (shouldUseFilters) {
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('RelatedAlertsBySession', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -65,7 +65,7 @@ describe('RelatedAlertsBySession', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -85,7 +85,7 @@ describe('RelatedAlertsBySession', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -109,7 +109,7 @@ describe('RelatedAlertsBySession', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { isActiveTimeline } from '../../../../helpers';
|
||||
import type { BrowserFields } from '../../../containers/source';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline';
|
||||
import { useActionCellDataProvider } from '../table/use_action_cell_data_provider';
|
||||
|
@ -24,7 +25,7 @@ interface Props {
|
|||
browserFields: BrowserFields;
|
||||
data: TimelineEventsDetailsItem;
|
||||
eventId: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -35,12 +36,12 @@ interface Props {
|
|||
* the related alerts in a timeline investigation.
|
||||
*/
|
||||
export const RelatedAlertsBySession = React.memo<Props>(
|
||||
({ browserFields, data, eventId, timelineId }) => {
|
||||
({ browserFields, data, eventId, scopeId }) => {
|
||||
const { field, values } = data;
|
||||
const { error, count, alertIds } = useAlertPrevalence({
|
||||
field,
|
||||
value: values,
|
||||
timelineId: timelineId ?? '',
|
||||
isActiveTimelines: isActiveTimeline(scopeId),
|
||||
signalIndexName: null,
|
||||
includeAlertIds: true,
|
||||
ignoreTimerange: true,
|
||||
|
@ -48,17 +49,17 @@ export const RelatedAlertsBySession = React.memo<Props>(
|
|||
|
||||
const { fieldFromBrowserField } = getEnrichedFieldInfo({
|
||||
browserFields,
|
||||
contextId: timelineId,
|
||||
contextId: scopeId,
|
||||
eventId,
|
||||
field: { id: data.field },
|
||||
timelineId,
|
||||
scopeId,
|
||||
item: data,
|
||||
});
|
||||
|
||||
const cellData = useActionCellDataProvider({
|
||||
field,
|
||||
values,
|
||||
contextId: timelineId,
|
||||
contextId: scopeId,
|
||||
eventId,
|
||||
fieldFromBrowserField,
|
||||
fieldFormat: fieldFromBrowserField?.format,
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('RelatedAlertsBySourceEvent', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -65,7 +65,7 @@ describe('RelatedAlertsBySourceEvent', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -85,7 +85,7 @@ describe('RelatedAlertsBySourceEvent', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -109,7 +109,7 @@ describe('RelatedAlertsBySourceEvent', () => {
|
|||
browserFields={{}}
|
||||
data={testData}
|
||||
eventId={testEventId}
|
||||
timelineId=""
|
||||
scopeId=""
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { isActiveTimeline } from '../../../../helpers';
|
||||
import type { BrowserFields } from '../../../containers/source';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../common/search_strategy/timeline';
|
||||
import { useActionCellDataProvider } from '../table/use_action_cell_data_provider';
|
||||
|
@ -29,7 +30,7 @@ interface Props {
|
|||
browserFields: BrowserFields;
|
||||
data: TimelineEventsDetailsItem;
|
||||
eventId: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,29 +41,29 @@ interface Props {
|
|||
* the related alerts in a timeline investigation.
|
||||
*/
|
||||
export const RelatedAlertsBySourceEvent = React.memo<Props>(
|
||||
({ browserFields, data, eventId, timelineId }) => {
|
||||
({ browserFields, data, eventId, scopeId }) => {
|
||||
const { field, values } = data;
|
||||
const { error, count, alertIds } = useAlertPrevalence({
|
||||
field,
|
||||
value: values,
|
||||
timelineId: timelineId ?? '',
|
||||
isActiveTimelines: isActiveTimeline(scopeId),
|
||||
signalIndexName: null,
|
||||
includeAlertIds: true,
|
||||
});
|
||||
|
||||
const { fieldFromBrowserField } = getEnrichedFieldInfo({
|
||||
browserFields,
|
||||
contextId: timelineId,
|
||||
contextId: scopeId,
|
||||
eventId,
|
||||
field: { id: data.field },
|
||||
timelineId,
|
||||
scopeId,
|
||||
item: data,
|
||||
});
|
||||
|
||||
const cellData = useActionCellDataProvider({
|
||||
field,
|
||||
values,
|
||||
contextId: timelineId,
|
||||
contextId: scopeId,
|
||||
eventId,
|
||||
fieldFromBrowserField,
|
||||
fieldFormat: fieldFromBrowserField?.format,
|
||||
|
|
|
@ -77,10 +77,10 @@ describe('Event Details Overview Cards', () => {
|
|||
|
||||
const props = {
|
||||
handleOnEventClosed: jest.fn(),
|
||||
contextId: 'detections-page',
|
||||
contextId: 'alerts-page',
|
||||
eventId: 'testId',
|
||||
indexName: 'testIndex',
|
||||
timelineId: 'page',
|
||||
scopeId: 'page',
|
||||
data: [
|
||||
{
|
||||
category: 'kibana',
|
||||
|
|
|
@ -43,7 +43,7 @@ interface Props {
|
|||
eventId: string;
|
||||
handleOnEventClosed: () => void;
|
||||
indexName: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
isReadOnly?: boolean;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ export const Overview = React.memo<Props>(
|
|||
eventId,
|
||||
handleOnEventClosed,
|
||||
indexName,
|
||||
timelineId,
|
||||
scopeId,
|
||||
isReadOnly,
|
||||
}) => {
|
||||
const statusData = useMemo(() => {
|
||||
|
@ -65,12 +65,12 @@ export const Overview = React.memo<Props>(
|
|||
getEnrichedFieldInfo({
|
||||
eventId,
|
||||
contextId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
browserFields,
|
||||
item,
|
||||
})
|
||||
);
|
||||
}, [browserFields, contextId, data, eventId, timelineId]);
|
||||
}, [browserFields, contextId, data, eventId, scopeId]);
|
||||
|
||||
const severityData = useMemo(() => {
|
||||
const item = find({ field: 'kibana.alert.severity', category: 'kibana' }, data);
|
||||
|
@ -79,12 +79,12 @@ export const Overview = React.memo<Props>(
|
|||
getEnrichedFieldInfo({
|
||||
eventId,
|
||||
contextId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
browserFields,
|
||||
item,
|
||||
})
|
||||
);
|
||||
}, [browserFields, contextId, data, eventId, timelineId]);
|
||||
}, [browserFields, contextId, data, eventId, scopeId]);
|
||||
|
||||
const riskScoreData = useMemo(() => {
|
||||
const item = find({ field: 'kibana.alert.risk_score', category: 'kibana' }, data);
|
||||
|
@ -93,12 +93,12 @@ export const Overview = React.memo<Props>(
|
|||
getEnrichedFieldInfo({
|
||||
eventId,
|
||||
contextId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
browserFields,
|
||||
item,
|
||||
})
|
||||
);
|
||||
}, [browserFields, contextId, data, eventId, timelineId]);
|
||||
}, [browserFields, contextId, data, eventId, scopeId]);
|
||||
|
||||
const ruleNameData = useMemo(() => {
|
||||
const item = find({ field: SIGNAL_RULE_NAME_FIELD_NAME, category: 'kibana' }, data);
|
||||
|
@ -108,13 +108,13 @@ export const Overview = React.memo<Props>(
|
|||
getEnrichedFieldInfo({
|
||||
eventId,
|
||||
contextId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
browserFields,
|
||||
item,
|
||||
linkValueField,
|
||||
})
|
||||
);
|
||||
}, [browserFields, contextId, data, eventId, timelineId]);
|
||||
}, [browserFields, contextId, data, eventId, scopeId]);
|
||||
|
||||
const signalCard =
|
||||
hasData(statusData) && !isReadOnly ? (
|
||||
|
@ -129,7 +129,7 @@ export const Overview = React.memo<Props>(
|
|||
contextId={contextId}
|
||||
enrichedFieldInfo={statusData}
|
||||
indexName={indexName}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
handleOnEventClosed={handleOnEventClosed}
|
||||
/>
|
||||
</OverviewCardWithActions>
|
||||
|
|
|
@ -8,8 +8,40 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { OverviewCardWithActions } from './overview_card';
|
||||
import { TestProviders } from '../../../mock';
|
||||
import {
|
||||
createSecuritySolutionStorageMock,
|
||||
kibanaObservable,
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
TestProviders,
|
||||
} from '../../../mock';
|
||||
import { SeverityBadge } from '../../../../detections/components/rules/severity_badge';
|
||||
import type { State } from '../../../store';
|
||||
import { createStore } from '../../../store';
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const state: State = {
|
||||
...mockGlobalState,
|
||||
timeline: {
|
||||
...mockGlobalState.timeline,
|
||||
timelineById: {
|
||||
[TimelineId.casePage]: {
|
||||
...mockGlobalState.timeline.timelineById[TimelineId.test],
|
||||
id: TimelineId.casePage,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
const props = {
|
||||
title: 'Severity',
|
||||
|
@ -18,7 +50,6 @@ const props = {
|
|||
contextId: 'timeline-case',
|
||||
eventId: 'testid',
|
||||
fieldType: 'string',
|
||||
timelineId: 'timeline-case',
|
||||
data: {
|
||||
field: 'kibana.alert.rule.severity',
|
||||
format: 'string',
|
||||
|
@ -44,6 +75,7 @@ const props = {
|
|||
example: '',
|
||||
fields: {},
|
||||
},
|
||||
scopeId: 'timeline-case',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -52,7 +84,7 @@ jest.mock('../../../lib/kibana');
|
|||
describe('OverviewCardWithActions', () => {
|
||||
test('it renders correctly', () => {
|
||||
const { getByText } = render(
|
||||
<TestProviders>
|
||||
<TestProviders store={store}>
|
||||
<OverviewCardWithActions {...props}>
|
||||
<SeverityBadge value="medium" />
|
||||
</OverviewCardWithActions>
|
||||
|
|
|
@ -13,12 +13,12 @@ import { TestProviders } from '../../../mock';
|
|||
import { useAlertsPrivileges } from '../../../../detections/containers/detection_engine/alerts/use_alerts_privileges';
|
||||
const props = {
|
||||
eventId: 'testid',
|
||||
contextId: 'detections-page',
|
||||
contextId: 'alerts-page',
|
||||
enrichedFieldInfo: {
|
||||
contextId: 'detections-page',
|
||||
contextId: 'alerts-page',
|
||||
eventId: 'testid',
|
||||
fieldType: 'string',
|
||||
timelineId: 'detections-page',
|
||||
scopeId: 'alerts-page',
|
||||
data: {
|
||||
field: 'kibana.alert.workflow_status',
|
||||
format: 'string',
|
||||
|
@ -46,7 +46,7 @@ const props = {
|
|||
},
|
||||
},
|
||||
indexName: '.internal.alerts-security.alerts-default-000001',
|
||||
timelineId: 'detections-page',
|
||||
scopeId: 'alerts-page',
|
||||
handleOnEventClosed: jest.fn(),
|
||||
};
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ interface StatusPopoverButtonProps {
|
|||
contextId: string;
|
||||
enrichedFieldInfo: EnrichedFieldInfoWithValues;
|
||||
indexName: string;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
handleOnEventClosed: () => void;
|
||||
}
|
||||
|
||||
export const StatusPopoverButton = React.memo<StatusPopoverButtonProps>(
|
||||
({ eventId, contextId, enrichedFieldInfo, indexName, timelineId, handleOnEventClosed }) => {
|
||||
({ eventId, contextId, enrichedFieldInfo, indexName, scopeId, handleOnEventClosed }) => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
||||
const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]);
|
||||
const closePopover = useCallback(() => setIsPopoverOpen(false), []);
|
||||
|
@ -39,7 +39,7 @@ export const StatusPopoverButton = React.memo<StatusPopoverButtonProps>(
|
|||
const { actionItems } = useAlertsActions({
|
||||
closePopover: closeAfterAction,
|
||||
eventId,
|
||||
timelineId,
|
||||
scopeId,
|
||||
indexName,
|
||||
alertStatus: enrichedFieldInfo.values[0] as Status,
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ const enrichedHostIpData: AlertSummaryRow['description'] = {
|
|||
eventId,
|
||||
fieldFromBrowserField: { ...hostIpFieldFromBrowserField },
|
||||
isDraggable: false,
|
||||
timelineId: TimelineId.test,
|
||||
scopeId: TimelineId.test,
|
||||
values: [...hostIpValues],
|
||||
};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React, { useCallback, useState, useContext } from 'react';
|
||||
import { TimelineContext } from '@kbn/timelines-plugin/public';
|
||||
import { TimelineContext } from '../../../../timelines/components/timeline';
|
||||
import { HoverActions } from '../../hover_actions';
|
||||
import { useActionCellDataProvider } from './use_action_cell_data_provider';
|
||||
import type { EnrichedFieldInfo } from '../types';
|
||||
|
@ -34,7 +34,7 @@ export const ActionCell: React.FC<Props> = React.memo(
|
|||
linkValue,
|
||||
onFilterAdded,
|
||||
setIsPopoverVisible,
|
||||
timelineId,
|
||||
scopeId,
|
||||
toggleColumn,
|
||||
values,
|
||||
hideAddToTimeline,
|
||||
|
@ -83,7 +83,7 @@ export const ActionCell: React.FC<Props> = React.memo(
|
|||
onFilterAdded={onFilterAdded}
|
||||
ownFocus={hoverActionsOwnFocus}
|
||||
showTopN={showTopN}
|
||||
timelineId={timelineId ?? timelineIdFind}
|
||||
scopeId={scopeId ?? timelineIdFind}
|
||||
toggleColumn={toggleColumn}
|
||||
toggleTopN={toggleTopN}
|
||||
values={actionCellConfig?.values}
|
||||
|
|
|
@ -54,7 +54,7 @@ const enrichedHostIpData: AlertSummaryRow['description'] = {
|
|||
eventId,
|
||||
fieldFromBrowserField: { ...hostIpFieldFromBrowserField },
|
||||
isDraggable: false,
|
||||
timelineId: TimelineId.test,
|
||||
scopeId: TimelineId.test,
|
||||
values: [...hostIpValues],
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
import type { AlertSummaryRow } from '../helpers';
|
||||
import { getEmptyTagValue } from '../../empty_value';
|
||||
import { InvestigateInTimelineButton } from './investigate_in_timeline_button';
|
||||
|
@ -22,18 +23,18 @@ const PrevalenceCell: React.FC<AlertSummaryRow['description']> = ({
|
|||
eventId,
|
||||
fieldFromBrowserField,
|
||||
linkValue,
|
||||
timelineId,
|
||||
scopeId,
|
||||
values,
|
||||
}) => {
|
||||
const { loading, count } = useAlertPrevalence({
|
||||
field: data.field,
|
||||
timelineId,
|
||||
isActiveTimelines: scopeId === TimelineId.active,
|
||||
value: values,
|
||||
signalIndexName: null,
|
||||
});
|
||||
|
||||
const cellDataProviders = useActionCellDataProvider({
|
||||
contextId: timelineId,
|
||||
contextId: scopeId,
|
||||
eventId,
|
||||
field: data.field,
|
||||
fieldFormat: data.format,
|
||||
|
|
|
@ -49,7 +49,7 @@ const enrichedHostIpData: AlertSummaryRow['description'] = {
|
|||
eventId,
|
||||
fieldFromBrowserField: { ...hostIpFieldFromBrowserField },
|
||||
isDraggable: false,
|
||||
timelineId: TimelineId.test,
|
||||
scopeId: TimelineId.test,
|
||||
values: [...hostIpValues],
|
||||
};
|
||||
|
||||
|
@ -71,7 +71,7 @@ const enrichedAgentStatusData: AlertSummaryRow['description'] = {
|
|||
},
|
||||
eventId,
|
||||
values: [],
|
||||
timelineId: TimelineId.test,
|
||||
scopeId: TimelineId.test,
|
||||
};
|
||||
|
||||
describe('SummaryValueCell', () => {
|
||||
|
@ -90,7 +90,7 @@ describe('SummaryValueCell', () => {
|
|||
test('When in the timeline flyout with timelineId active', async () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<SummaryValueCell {...enrichedHostIpData} timelineId={TimelineId.active} />
|
||||
<SummaryValueCell {...enrichedHostIpData} scopeId={TimelineId.active} />
|
||||
</TestProviders>
|
||||
);
|
||||
hostIpValues.forEach((ipValue) => expect(screen.getByText(ipValue)).toBeInTheDocument());
|
||||
|
|
|
@ -21,7 +21,7 @@ export const SummaryValueCell: React.FC<AlertSummaryRow['description']> = ({
|
|||
fieldFromBrowserField,
|
||||
isDraggable,
|
||||
linkValue,
|
||||
timelineId,
|
||||
scopeId,
|
||||
values,
|
||||
isReadOnly,
|
||||
}) => {
|
||||
|
@ -30,7 +30,7 @@ export const SummaryValueCell: React.FC<AlertSummaryRow['description']> = ({
|
|||
return (
|
||||
<>
|
||||
<FieldValueCell
|
||||
contextId={timelineId}
|
||||
contextId={scopeId}
|
||||
data={data}
|
||||
eventId={eventId}
|
||||
fieldFromBrowserField={fieldFromBrowserField}
|
||||
|
@ -39,14 +39,14 @@ export const SummaryValueCell: React.FC<AlertSummaryRow['description']> = ({
|
|||
style={style}
|
||||
values={values}
|
||||
/>
|
||||
{timelineId !== TimelineId.active && !isReadOnly && hoverActionsEnabled && (
|
||||
{scopeId !== TimelineId.active && !isReadOnly && hoverActionsEnabled && (
|
||||
<ActionCell
|
||||
contextId={timelineId}
|
||||
contextId={scopeId}
|
||||
data={data}
|
||||
eventId={eventId}
|
||||
fieldFromBrowserField={fieldFromBrowserField}
|
||||
linkValue={linkValue}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
values={values}
|
||||
applyWidthAndPadding={false}
|
||||
hideAddToTimeline={false}
|
||||
|
|
|
@ -21,7 +21,7 @@ export interface EnrichedFieldInfo {
|
|||
data: FieldsData | EventFieldsData;
|
||||
eventId: string;
|
||||
fieldFromBrowserField?: BrowserField;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
values: string[] | null | undefined;
|
||||
linkValue?: string;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { TableId } from '../../../../common/types';
|
||||
import { HostsType } from '../../../hosts/store/model';
|
||||
import { TestProviders } from '../../mock';
|
||||
import type { EventsQueryTabBodyComponentProps } from './events_query_tab_body';
|
||||
|
@ -76,7 +76,7 @@ describe('EventsQueryTabBody', () => {
|
|||
const commonProps: EventsQueryTabBodyComponentProps = {
|
||||
indexNames: ['test-index'],
|
||||
setQuery: jest.fn(),
|
||||
timelineId: TimelineId.test,
|
||||
tableId: TableId.test,
|
||||
type: HostsType.page,
|
||||
endDate: new Date('2000').toISOString(),
|
||||
startDate: new Date('2000').toISOString(),
|
||||
|
|
|
@ -12,10 +12,10 @@ import { EuiCheckbox } from '@elastic/eui';
|
|||
import type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
|
||||
import type { TimelineId } from '../../../../common/types/timeline';
|
||||
import { dataTableActions } from '../../store/data_table';
|
||||
import type { TableId } from '../../../../common/types/timeline';
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { StatefulEventsViewer } from '../events_viewer';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import { eventsDefaultModel } from '../events_viewer/default_model';
|
||||
import { MatrixHistogram } from '../matrix_histogram';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
|
@ -57,7 +57,7 @@ export type EventsQueryTabBodyComponentProps = QueryTabBodyProps & {
|
|||
pageFilters?: Filter[];
|
||||
externalAlertPageFilters?: Filter[];
|
||||
setQuery: GlobalTimeArgs['setQuery'];
|
||||
timelineId: TimelineId;
|
||||
tableId: TableId;
|
||||
};
|
||||
|
||||
const EXTERNAL_ALERTS_URL_PARAM = 'onlyExternalAlerts';
|
||||
|
@ -71,7 +71,7 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
pageFilters = [],
|
||||
setQuery,
|
||||
startDate,
|
||||
timelineId,
|
||||
tableId,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { globalFullScreen } = useGlobalFullScreen();
|
||||
|
@ -100,8 +100,8 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
timelineActions.initializeTGridSettings({
|
||||
id: timelineId,
|
||||
dataTableActions.initializeTGridSettings({
|
||||
id: tableId,
|
||||
defaultColumns: eventsDefaultModel.columns.map((c) =>
|
||||
!tGridEnabled && c.initialWidth == null
|
||||
? {
|
||||
|
@ -110,10 +110,9 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
}
|
||||
: c
|
||||
),
|
||||
excludedRowRendererIds: showExternalAlerts ? Object.values(RowRendererId) : undefined,
|
||||
})
|
||||
);
|
||||
}, [dispatch, showExternalAlerts, tGridEnabled, timelineId]);
|
||||
}, [dispatch, showExternalAlerts, tGridEnabled, tableId]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
@ -178,7 +177,7 @@ const EventsQueryTabBodyComponent: React.FC<EventsQueryTabBodyComponentProps> =
|
|||
renderCellValue={DefaultCellRenderer}
|
||||
rowRenderers={defaultRowRenderers}
|
||||
scopeId={SourcererScopeName.default}
|
||||
id={timelineId}
|
||||
tableId={tableId}
|
||||
unit={showExternalAlerts ? i18n.ALERTS_UNIT : i18n.EVENTS_UNIT}
|
||||
defaultModel={defaultModel}
|
||||
pageFilters={composedPageFilters}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { tableDefaults } from '../../store/data_table/defaults';
|
||||
import type { SubsetTGridModel } from '../../store/data_table/model';
|
||||
import { defaultEventHeaders } from './default_event_headers';
|
||||
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
|
||||
export const eventsDefaultModel: SubsetTimelineModel = {
|
||||
...timelineDefaults,
|
||||
export const eventsDefaultModel: SubsetTGridModel = {
|
||||
...tableDefaults,
|
||||
columns: defaultEventHeaders,
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ import { mockEventViewerResponse } from './mock';
|
|||
import { StatefulEventsViewer } from '.';
|
||||
import { eventsDefaultModel } from './default_model';
|
||||
import { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TableId } from '../../../../common/types/timeline';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
|
||||
import { useTimelineEvents } from '../../../timelines/containers';
|
||||
|
@ -60,7 +60,7 @@ const testProps = {
|
|||
end: to,
|
||||
entityType: EntityType.ALERTS,
|
||||
indexNames: [],
|
||||
id: TimelineId.test,
|
||||
tableId: TableId.test,
|
||||
leadingControlColumns: getDefaultControlColumn(ACTION_BUTTON_COUNT),
|
||||
renderCellValue: DefaultCellRenderer,
|
||||
rowRenderers: defaultRowRenderers,
|
||||
|
|
|
@ -9,17 +9,15 @@ import React, { useRef, useCallback, useMemo, useEffect } from 'react';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import type { EntityType, RowRenderer } from '@kbn/timelines-plugin/common';
|
||||
import type { TGridCellAction } from '@kbn/timelines-plugin/common/types';
|
||||
import type { ControlColumnProps, TableId } from '../../../../common/types';
|
||||
import { dataTableActions } from '../../store/data_table';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { useBulkAddToCaseActions } from '../../../detections/components/alerts_table/timeline_actions/use_bulk_add_to_case_actions';
|
||||
import type { inputsModel, State } from '../../store';
|
||||
import { inputsActions } from '../../store/actions';
|
||||
import type { ControlColumnProps, RowRenderer } from '../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { APP_UI_ID } from '../../../../common/constants';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import type { Status } from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { InspectButtonContainer } from '../inspect';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
|
@ -38,6 +36,7 @@ import {
|
|||
useSessionViewNavigation,
|
||||
useSessionView,
|
||||
} from '../../../timelines/components/timeline/session_tab_content/use_session_view';
|
||||
import type { SubsetTGridModel } from '../../store/data_table/model';
|
||||
|
||||
const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = [];
|
||||
|
||||
|
@ -50,10 +49,10 @@ const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>`
|
|||
|
||||
export interface Props {
|
||||
defaultCellActions?: TGridCellAction[];
|
||||
defaultModel: SubsetTimelineModel;
|
||||
defaultModel: SubsetTGridModel;
|
||||
end: string;
|
||||
entityType: EntityType;
|
||||
id: TimelineId;
|
||||
tableId: TableId;
|
||||
leadingControlColumns: ControlColumnProps[];
|
||||
scopeId: SourcererScopeName;
|
||||
start: string;
|
||||
|
@ -78,7 +77,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
defaultModel,
|
||||
end,
|
||||
entityType,
|
||||
id,
|
||||
tableId,
|
||||
leadingControlColumns,
|
||||
pageFilters,
|
||||
currentFilter,
|
||||
|
@ -97,22 +96,18 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
input,
|
||||
query,
|
||||
globalQueries,
|
||||
timelineQuery,
|
||||
timeline: {
|
||||
dataTable: {
|
||||
columns,
|
||||
dataProviders,
|
||||
defaultColumns,
|
||||
deletedEventIds,
|
||||
excludedRowRendererIds,
|
||||
graphEventId, // If truthy, the graph viewer (Resolver) is showing
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
sessionViewConfig,
|
||||
showCheckboxes,
|
||||
sort,
|
||||
} = defaultModel,
|
||||
} = useSelector((state: State) => eventsViewerSelector(state, id));
|
||||
} = useSelector((state: State) => eventsViewerSelector(state, tableId));
|
||||
|
||||
const { timelines: timelinesUi } = useKibana().services;
|
||||
const {
|
||||
|
@ -133,12 +128,11 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
timelineActions.createTimeline({
|
||||
dataTableActions.createTGrid({
|
||||
columns,
|
||||
dataViewId: selectedDataViewId,
|
||||
defaultColumns,
|
||||
excludedRowRendererIds,
|
||||
id,
|
||||
id: tableId,
|
||||
indexNames: selectedPatterns,
|
||||
itemsPerPage,
|
||||
showCheckboxes,
|
||||
|
@ -147,7 +141,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
);
|
||||
|
||||
return () => {
|
||||
dispatch(inputsActions.deleteOneQuery({ id, inputId: InputsModelId.global }));
|
||||
dispatch(inputsActions.deleteOneQuery({ id: tableId, inputId: InputsModelId.global }));
|
||||
if (editorActionsRef.current) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
editorActionsRef.current.closeEditor();
|
||||
|
@ -160,28 +154,34 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
const trailingControlColumns: ControlColumnProps[] = EMPTY_CONTROL_COLUMNS;
|
||||
|
||||
const { Navigation } = useSessionViewNavigation({
|
||||
timelineId: id,
|
||||
scopeId: tableId,
|
||||
});
|
||||
|
||||
const { DetailsPanel, SessionView } = useSessionView({
|
||||
entityType,
|
||||
timelineId: id,
|
||||
scopeId: tableId,
|
||||
});
|
||||
|
||||
const graphOverlay = useMemo(() => {
|
||||
const shouldShowOverlay =
|
||||
(graphEventId != null && graphEventId.length > 0) || sessionViewConfig != null;
|
||||
return shouldShowOverlay ? (
|
||||
<GraphOverlay timelineId={id} SessionView={SessionView} Navigation={Navigation} />
|
||||
<GraphOverlay scopeId={tableId} SessionView={SessionView} Navigation={Navigation} />
|
||||
) : null;
|
||||
}, [graphEventId, id, sessionViewConfig, SessionView, Navigation]);
|
||||
}, [graphEventId, tableId, sessionViewConfig, SessionView, Navigation]);
|
||||
const setQuery = useCallback(
|
||||
(inspect, loading, refetch) => {
|
||||
dispatch(
|
||||
inputsActions.setQuery({ id, inputId: InputsModelId.global, inspect, loading, refetch })
|
||||
inputsActions.setQuery({
|
||||
id: tableId,
|
||||
inputId: InputsModelId.global,
|
||||
inspect,
|
||||
loading,
|
||||
refetch,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch, id]
|
||||
[dispatch, tableId]
|
||||
);
|
||||
|
||||
const refetchQuery = (newQueries: inputsModel.GlobalQuery[]) => {
|
||||
|
@ -192,25 +192,22 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
const bulkActions = useMemo(
|
||||
() => ({
|
||||
onAlertStatusActionSuccess: () => {
|
||||
if (id === TimelineId.active) {
|
||||
refetchQuery([timelineQuery]);
|
||||
} else {
|
||||
refetchQuery(globalQueries);
|
||||
}
|
||||
refetchQuery(globalQueries);
|
||||
},
|
||||
customBulkActions: addToCaseBulkActions,
|
||||
}),
|
||||
[addToCaseBulkActions, globalQueries, id, timelineQuery]
|
||||
[addToCaseBulkActions, globalQueries]
|
||||
);
|
||||
|
||||
const fieldBrowserOptions = useFieldBrowserOptions({
|
||||
sourcererScope: scopeId,
|
||||
timelineId: id,
|
||||
editorActionsRef,
|
||||
upsertColumn: (column, index) =>
|
||||
dispatch(dataTableActions.upsertColumn({ column, id: tableId, index })),
|
||||
removeColumn: (columnId) => dispatch(dataTableActions.removeColumn({ columnId, id: tableId })),
|
||||
});
|
||||
|
||||
const isLive = input.policy.kind === 'interval';
|
||||
|
||||
return (
|
||||
<>
|
||||
<FullScreenContainer $isFullScreen={globalFullScreen}>
|
||||
|
@ -221,7 +218,6 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
browserFields,
|
||||
bulkActions,
|
||||
columns,
|
||||
dataProviders,
|
||||
dataViewId,
|
||||
defaultCellActions,
|
||||
deletedEventIds,
|
||||
|
@ -236,14 +232,13 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
graphEventId,
|
||||
graphOverlay,
|
||||
hasAlertsCrud,
|
||||
id,
|
||||
id: tableId,
|
||||
indexNames: selectedPatterns,
|
||||
indexPattern,
|
||||
isLive,
|
||||
isLoadingIndexPattern,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
leadingControlColumns,
|
||||
onRuleChange,
|
||||
query,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { eventsViewerSelector } from '.';
|
|||
describe('selectors', () => {
|
||||
describe('eventsViewerSelector', () => {
|
||||
it('returns the expected results', () => {
|
||||
const id = 'detections-page';
|
||||
const id = 'alerts-page';
|
||||
|
||||
expect(eventsViewerSelector(mockState, id)).toEqual({
|
||||
filters: mockState.inputs.global.filters,
|
||||
|
@ -20,7 +20,7 @@ describe('selectors', () => {
|
|||
query: mockState.inputs.global.query,
|
||||
globalQueries: mockState.inputs.global.queries,
|
||||
timelineQuery: mockState.inputs.timeline.queries[0],
|
||||
timeline: mockState.timeline.timelineById[id],
|
||||
dataTable: mockState.dataTable.tableById[id],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { getTableByIdSelector } from '../../../store/data_table/selectors';
|
||||
import {
|
||||
getTimelineSelector,
|
||||
globalFiltersQuerySelector,
|
||||
|
@ -14,12 +15,11 @@ import {
|
|||
globalQuerySelector,
|
||||
timelineQueryByIdSelector,
|
||||
} from '../../../store/inputs/selectors';
|
||||
import { getTimelineByIdSelector } from '../../../../timelines/store/timeline/selectors';
|
||||
|
||||
/**
|
||||
* This selector is invoked with two arguments:
|
||||
* @param state - the state of the store as defined by State in common/store/types.ts
|
||||
* @param id - a timeline id e.g. `detections-page`
|
||||
* @param id - a timeline id e.g. `alerts-page`
|
||||
*
|
||||
* Example:
|
||||
* `useSelector((state: State) => eventsViewerSelector(state, id))`
|
||||
|
@ -30,8 +30,8 @@ export const eventsViewerSelector = createSelector(
|
|||
globalQuerySelector(),
|
||||
globalQuery(),
|
||||
timelineQueryByIdSelector(),
|
||||
getTimelineByIdSelector(),
|
||||
(filters, input, query, globalQueries, timelineQuery, timeline) => ({
|
||||
getTableByIdSelector(),
|
||||
(filters, input, query, globalQueries, timelineQuery, dataTable) => ({
|
||||
/** an array representing filters added to the search bar */
|
||||
filters,
|
||||
/** an object containing the timerange set in the global date picker, and other page level state */
|
||||
|
@ -42,7 +42,7 @@ export const eventsViewerSelector = createSelector(
|
|||
globalQueries,
|
||||
/** an object with metadata and actions related to the table query */
|
||||
timelineQuery,
|
||||
/** a specific timeline from the state's timelineById collection, or undefined */
|
||||
timeline,
|
||||
/** a specific data table from the state's tableById collection, or undefined */
|
||||
dataTable,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -54,7 +54,7 @@ const query = { query: 'host.ip: *', language: 'kuery' };
|
|||
|
||||
const globalQueries = [
|
||||
{
|
||||
id: 'detections-page',
|
||||
id: 'alerts-page',
|
||||
inspect: {
|
||||
dsl: ['{\n "allow_no_indices": ...}'],
|
||||
},
|
||||
|
@ -86,7 +86,7 @@ const globalQueries = [
|
|||
|
||||
const timelineQueries = [
|
||||
{
|
||||
id: 'detections-page',
|
||||
id: 'alerts-page',
|
||||
inspect: {
|
||||
dsl: ['{\n "allow_no_indices": ...}'],
|
||||
},
|
||||
|
@ -97,7 +97,7 @@ const timelineQueries = [
|
|||
];
|
||||
|
||||
const timeline = {
|
||||
id: 'detections-page',
|
||||
id: 'alerts-page',
|
||||
columns: [
|
||||
{ columnHeaderType: 'not-filtered', id: '@timestamp', initialWidth: 200 },
|
||||
{
|
||||
|
@ -201,7 +201,6 @@ const timeline = {
|
|||
sort: [{ columnId: '@timestamp', columnType: 'date', sortDirection: 'desc' }],
|
||||
savedObjectId: null,
|
||||
version: null,
|
||||
footerText: 'alerts',
|
||||
title: '',
|
||||
initialized: true,
|
||||
activeTab: 'query',
|
||||
|
@ -244,5 +243,5 @@ export const mockState = pipe(
|
|||
(state) => set(state, 'inputs.global.query', query),
|
||||
(state) => set(state, 'inputs.global.queries', globalQueries),
|
||||
(state) => set(state, 'inputs.timeline.queries', timelineQueries),
|
||||
(state) => set(state, 'timeline.timelineById.detections-page', timeline)
|
||||
(state) => set(state, 'timeline.timelineById.alerts-page', timeline)
|
||||
)(mockGlobalState);
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context';
|
||||
import { TestProviders } from '../../../mock';
|
||||
import { ShowTopNButton } from './show_top_n';
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
|
||||
jest.mock('../../visualization_actions', () => ({
|
||||
VisualizationActions: jest.fn(() => <div data-test-subj="mock-viz-actions" />),
|
||||
|
@ -39,7 +40,7 @@ describe('show topN button', () => {
|
|||
onClick: jest.fn(),
|
||||
ownFocus: false,
|
||||
showTopN: false,
|
||||
timelineId: 'timeline-1',
|
||||
scopeId: TimelineId.active,
|
||||
value: ['rule_name'],
|
||||
};
|
||||
|
||||
|
@ -178,9 +179,7 @@ describe('show topN button', () => {
|
|||
expect(wrapper.find('[data-test-subj="top-n"]').prop('toggleTopN')).toEqual(
|
||||
testProps.onClick
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="top-n"]').prop('timelineId')).toEqual(
|
||||
testProps.timelineId
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="top-n"]').prop('scopeId')).toEqual(testProps.scopeId);
|
||||
expect(wrapper.find('[data-test-subj="top-n"]').prop('onFilterAdded')).toEqual(
|
||||
testProps.onFilterAdded
|
||||
);
|
||||
|
|
|
@ -10,13 +10,14 @@ import type { EuiButtonEmpty, EuiContextMenuItem } from '@elastic/eui';
|
|||
import { EuiPopover, EuiButtonIcon, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
import { isActiveTimeline } from '../../../../helpers';
|
||||
import { StatefulTopN } from '../../top_n';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import { SourcererScopeName } from '../../../store/sourcerer/model';
|
||||
import { useSourcererDataView } from '../../../containers/sourcerer';
|
||||
import { TooltipWithKeyboardShortcut } from '../../accessibility';
|
||||
import { getAdditionalScreenReaderOnlyContext } from '../utils';
|
||||
import { SHOW_TOP_N_KEYBOARD_SHORTCUT } from '../keyboard_shortcut_constants';
|
||||
import { isDetectionsAlertsTable } from '../../top_n/helpers';
|
||||
|
||||
const SHOW_TOP = (fieldName: string) =>
|
||||
i18n.translate('xpack.securitySolution.hoverActions.showTopTooltip', {
|
||||
|
@ -44,7 +45,7 @@ interface Props {
|
|||
showTooltip?: boolean;
|
||||
showTopN: boolean;
|
||||
showLegend?: boolean;
|
||||
timelineId?: string | null;
|
||||
scopeId?: string | null;
|
||||
title?: string;
|
||||
value?: string[] | string | null;
|
||||
}
|
||||
|
@ -66,20 +67,16 @@ export const ShowTopNButton: React.FC<Props> = React.memo(
|
|||
showLegend,
|
||||
showTooltip = true,
|
||||
showTopN,
|
||||
timelineId,
|
||||
scopeId,
|
||||
title,
|
||||
value,
|
||||
globalFilters,
|
||||
}) => {
|
||||
const activeScope: SourcererScopeName =
|
||||
timelineId === TimelineId.active
|
||||
? SourcererScopeName.timeline
|
||||
: timelineId != null &&
|
||||
[TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage].includes(
|
||||
timelineId as TimelineId
|
||||
)
|
||||
? SourcererScopeName.detections
|
||||
: SourcererScopeName.default;
|
||||
const activeScope: SourcererScopeName = isActiveTimeline(scopeId ?? '')
|
||||
? SourcererScopeName.timeline
|
||||
: scopeId != null && isDetectionsAlertsTable(scopeId)
|
||||
? SourcererScopeName.detections
|
||||
: SourcererScopeName.default;
|
||||
const { browserFields, indexPattern } = useSourcererDataView(activeScope);
|
||||
|
||||
const icon = iconType ?? 'visBarVertical';
|
||||
|
@ -147,7 +144,7 @@ export const ShowTopNButton: React.FC<Props> = React.memo(
|
|||
onFilterAdded={onFilterAdded}
|
||||
paddingSize={paddingSize}
|
||||
showLegend={showLegend}
|
||||
timelineId={timelineId ?? undefined}
|
||||
scopeId={scopeId ?? undefined}
|
||||
toggleTopN={onClick}
|
||||
value={value}
|
||||
globalFilters={globalFilters}
|
||||
|
@ -160,7 +157,7 @@ export const ShowTopNButton: React.FC<Props> = React.memo(
|
|||
onFilterAdded,
|
||||
paddingSize,
|
||||
showLegend,
|
||||
timelineId,
|
||||
scopeId,
|
||||
onClick,
|
||||
value,
|
||||
globalFilters,
|
||||
|
|
|
@ -107,7 +107,7 @@ interface Props {
|
|||
ownFocus: boolean;
|
||||
showOwnFocus?: boolean;
|
||||
showTopN: boolean;
|
||||
timelineId?: string | null;
|
||||
scopeId?: string | null;
|
||||
toggleColumn?: (column: ColumnHeaderOptions) => void;
|
||||
toggleTopN: () => void;
|
||||
values?: string[] | string | null;
|
||||
|
@ -149,7 +149,7 @@ export const HoverActions: React.FC<Props> = React.memo(
|
|||
ownFocus,
|
||||
showOwnFocus = true,
|
||||
showTopN,
|
||||
timelineId,
|
||||
scopeId,
|
||||
toggleColumn,
|
||||
toggleTopN,
|
||||
values,
|
||||
|
@ -178,11 +178,11 @@ export const HoverActions: React.FC<Props> = React.memo(
|
|||
const defaultFocusedButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInit.current && goGetTimelineId != null && timelineId == null) {
|
||||
if (isInit.current && goGetTimelineId != null && scopeId == null) {
|
||||
isInit.current = false;
|
||||
goGetTimelineId(true);
|
||||
}
|
||||
}, [goGetTimelineId, timelineId]);
|
||||
}, [goGetTimelineId, scopeId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ownFocus) {
|
||||
|
@ -215,7 +215,7 @@ export const HoverActions: React.FC<Props> = React.memo(
|
|||
[ownFocus, toggleTopN]
|
||||
);
|
||||
|
||||
const isCaseView = timelineId === TimelineId.casePage;
|
||||
const isCaseView = scopeId === TimelineId.casePage;
|
||||
|
||||
const { overflowActionItems, allActionItems } = useHoverActionItems({
|
||||
dataProvider,
|
||||
|
@ -237,10 +237,10 @@ export const HoverActions: React.FC<Props> = React.memo(
|
|||
ownFocus,
|
||||
showTopN,
|
||||
stKeyboardEvent,
|
||||
timelineId,
|
||||
toggleColumn,
|
||||
toggleTopN,
|
||||
values,
|
||||
scopeId,
|
||||
});
|
||||
|
||||
const Container = applyWidthAndPadding
|
||||
|
|
|
@ -13,15 +13,15 @@ import { isEmpty } from 'lodash';
|
|||
|
||||
import { FilterManager } from '@kbn/data-plugin/public';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isActiveTimeline } from '../../../helpers';
|
||||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { allowTopN } from '../drag_and_drop/helpers';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import type { ColumnHeaderOptions, DataProvider } from '../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { ShowTopNButton } from './actions/show_top_n';
|
||||
import { addProvider } from '../../../timelines/store/timeline/actions';
|
||||
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
export interface UseHoverActionItemsProps {
|
||||
dataProvider?: DataProvider | DataProvider[];
|
||||
dataType?: string;
|
||||
|
@ -43,7 +43,7 @@ export interface UseHoverActionItemsProps {
|
|||
ownFocus: boolean;
|
||||
showTopN: boolean;
|
||||
stKeyboardEvent: React.KeyboardEvent<Element> | undefined;
|
||||
timelineId?: string | null;
|
||||
scopeId?: string | null;
|
||||
toggleColumn?: (column: ColumnHeaderOptions) => void;
|
||||
toggleTopN: () => void;
|
||||
values?: string[] | string | null;
|
||||
|
@ -75,7 +75,7 @@ export const useHoverActionItems = ({
|
|||
ownFocus,
|
||||
showTopN,
|
||||
stKeyboardEvent,
|
||||
timelineId,
|
||||
scopeId,
|
||||
toggleColumn,
|
||||
toggleTopN,
|
||||
values,
|
||||
|
@ -96,16 +96,17 @@ export const useHoverActionItems = ({
|
|||
() => kibana.services.data.query.filterManager,
|
||||
[kibana.services.data.query.filterManager]
|
||||
);
|
||||
const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []);
|
||||
const { filterManager: activeFilterManager } = useDeepEqualSelector((state) =>
|
||||
getManageTimeline(state, timelineId ?? '')
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
|
||||
const activeFilterManager = useDeepEqualSelector((state) =>
|
||||
isActiveTimeline(scopeId ?? '') ? getTimeline(state, scopeId ?? '')?.filterManager : undefined
|
||||
);
|
||||
const filterManager = useMemo(
|
||||
() =>
|
||||
timelineId === TimelineId.active
|
||||
isActiveTimeline(scopeId ?? '')
|
||||
? activeFilterManager ?? new FilterManager(uiSettings)
|
||||
: filterManagerBackup,
|
||||
[uiSettings, timelineId, activeFilterManager, filterManagerBackup]
|
||||
[scopeId, activeFilterManager, uiSettings, filterManagerBackup]
|
||||
);
|
||||
|
||||
/*
|
||||
|
@ -152,19 +153,19 @@ export const useHoverActionItems = ({
|
|||
ownFocus={ownFocus}
|
||||
showTopN={showTopN}
|
||||
showTooltip={enableOverflowButton ? false : true}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
value={values}
|
||||
/>
|
||||
),
|
||||
[
|
||||
enableOverflowButton,
|
||||
field,
|
||||
isCaseView,
|
||||
field,
|
||||
toggleTopN,
|
||||
onFilterAdded,
|
||||
ownFocus,
|
||||
showTopN,
|
||||
timelineId,
|
||||
toggleTopN,
|
||||
scopeId,
|
||||
values,
|
||||
]
|
||||
);
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import React, { useCallback, useMemo, useState, useRef, useContext } from 'react';
|
||||
import type { DraggableProvided, DraggableStateSnapshot } from 'react-beautiful-dnd';
|
||||
import { TimelineContext } from '@kbn/timelines-plugin/public';
|
||||
import { TableContext } from '@kbn/timelines-plugin/public';
|
||||
import { TimelineContext } from '../../../timelines/components/timeline';
|
||||
import { HoverActions } from '.';
|
||||
|
||||
import type { DataProvider } from '../../../../common/types';
|
||||
|
@ -34,7 +35,7 @@ interface Props {
|
|||
isDraggable?: boolean;
|
||||
inline?: boolean;
|
||||
render: RenderFunctionProp;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
truncate?: boolean;
|
||||
onFilterAdded?: () => void;
|
||||
}
|
||||
|
@ -47,14 +48,19 @@ export const useHoverActions = ({
|
|||
isDraggable,
|
||||
onFilterAdded,
|
||||
render,
|
||||
timelineId,
|
||||
scopeId,
|
||||
}: Props) => {
|
||||
const { timelineId: timelineIdFind } = useContext(TimelineContext);
|
||||
const { tableId: tableIdFind } = useContext(TableContext);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const keyboardHandlerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [closePopOverTrigger, setClosePopOverTrigger] = useState(false);
|
||||
const [showTopN, setShowTopN] = useState<boolean>(false);
|
||||
const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState<boolean>(false);
|
||||
const { timelineId: timelineIdFind } = useContext(TimelineContext);
|
||||
const id = useMemo(
|
||||
() => (!scopeId ? timelineIdFind ?? tableIdFind : scopeId),
|
||||
[scopeId, tableIdFind, timelineIdFind]
|
||||
);
|
||||
|
||||
const handleClosePopOverTrigger = useCallback(() => {
|
||||
setClosePopOverTrigger((prevClosePopOverTrigger) => !prevClosePopOverTrigger);
|
||||
|
@ -114,7 +120,7 @@ export const useHoverActions = ({
|
|||
ownFocus={hoverActionsOwnFocus}
|
||||
showOwnFocus={false}
|
||||
showTopN={showTopN}
|
||||
timelineId={timelineId ?? timelineIdFind}
|
||||
scopeId={id}
|
||||
toggleTopN={toggleTopN}
|
||||
values={
|
||||
typeof dataProvider.queryMatch.value !== 'number'
|
||||
|
@ -124,19 +130,18 @@ export const useHoverActions = ({
|
|||
/>
|
||||
);
|
||||
}, [
|
||||
closeTopN,
|
||||
dataProvider,
|
||||
fieldType,
|
||||
handleClosePopOverTrigger,
|
||||
hideTopN,
|
||||
hoverActionsOwnFocus,
|
||||
isAggregatable,
|
||||
isDraggable,
|
||||
onFilterAdded,
|
||||
render,
|
||||
showTopN,
|
||||
timelineId,
|
||||
timelineIdFind,
|
||||
dataProvider,
|
||||
render,
|
||||
closeTopN,
|
||||
handleClosePopOverTrigger,
|
||||
isDraggable,
|
||||
isAggregatable,
|
||||
fieldType,
|
||||
hideTopN,
|
||||
onFilterAdded,
|
||||
id,
|
||||
toggleTopN,
|
||||
]);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import { upsertQuery } from '../../store/inputs/helpers';
|
|||
import { InspectButton } from '.';
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
jest.mock('./modal', () => ({
|
||||
ModalInspectQuery: jest.fn(() => <div data-test-subj="mocker-modal" />),
|
||||
|
@ -41,13 +42,25 @@ describe('Inspect Button', () => {
|
|||
state: state.inputs,
|
||||
};
|
||||
|
||||
let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
let store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
describe('Render', () => {
|
||||
beforeEach(() => {
|
||||
const myState = cloneDeep(state);
|
||||
myState.inputs = upsertQuery(newQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
});
|
||||
test('Eui Empty Button', () => {
|
||||
const wrapper = mount(
|
||||
|
@ -153,7 +166,13 @@ describe('Inspect Button', () => {
|
|||
const myQuery = cloneDeep(newQuery);
|
||||
myQuery.inspect = null;
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -170,7 +189,13 @@ describe('Inspect Button', () => {
|
|||
response: ['my response'],
|
||||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -187,7 +212,13 @@ describe('Inspect Button', () => {
|
|||
response: [],
|
||||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -206,7 +237,13 @@ describe('Inspect Button', () => {
|
|||
response: ['my response'],
|
||||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
});
|
||||
test('Open Inspect Modal', () => {
|
||||
const wrapper = mount(
|
||||
|
@ -251,7 +288,13 @@ describe('Inspect Button', () => {
|
|||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
myState.inputs.global.queries[0].isInspected = true;
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -270,7 +313,13 @@ describe('Inspect Button', () => {
|
|||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
myState.inputs.global.queries[0].isInspected = false;
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -289,7 +338,13 @@ describe('Inspect Button', () => {
|
|||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
myState.inputs.global.queries[0].isInspected = true;
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
@ -308,7 +363,13 @@ describe('Inspect Button', () => {
|
|||
};
|
||||
myState.inputs = upsertQuery(myQuery);
|
||||
myState.inputs.global.queries[0].isInspected = true;
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<InspectButton queryId={newQuery.id} title="My title" />
|
||||
|
|
|
@ -57,7 +57,7 @@ export type MatrixHistogramComponentProps = MatrixHistogramProps &
|
|||
showLegend?: boolean;
|
||||
stackByOptions: MatrixHistogramOption[];
|
||||
subtitle?: string | GetSubTitle;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
title: string | GetTitle;
|
||||
};
|
||||
|
||||
|
@ -97,7 +97,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
|
|||
stackByOptions,
|
||||
startDate,
|
||||
subtitle,
|
||||
timelineId,
|
||||
scopeId,
|
||||
title,
|
||||
titleSize,
|
||||
yTickFormatter,
|
||||
|
@ -300,7 +300,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
|
|||
barChart={barChartData}
|
||||
configs={barchartConfigs}
|
||||
stackByField={selectedStackByOption.value}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
/>
|
||||
)
|
||||
) : null}
|
||||
|
|
|
@ -21,6 +21,7 @@ import { coreMock } from '@kbn/core/public/mocks';
|
|||
import { createStore } from '../../store';
|
||||
import { inputsActions } from '../../store/inputs';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const mockSetAppFilters = jest.fn();
|
||||
const mockFilterManager = new FilterManager(coreMock.createStart().uiSettings);
|
||||
|
@ -137,7 +138,13 @@ describe('SearchBarComponent', () => {
|
|||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -173,7 +180,13 @@ describe('SearchBarComponent', () => {
|
|||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -188,7 +201,13 @@ describe('SearchBarComponent', () => {
|
|||
|
||||
it('calls useUpdateUrlParam when query query changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -213,7 +232,13 @@ describe('SearchBarComponent', () => {
|
|||
|
||||
it('calls useUpdateUrlParam when filters change', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -249,7 +274,13 @@ describe('SearchBarComponent', () => {
|
|||
|
||||
it('calls useUpdateUrlParam when savedQuery changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -282,7 +313,13 @@ describe('SearchBarComponent', () => {
|
|||
describe('Timerange', () => {
|
||||
it('calls useUpdateUrlParam when global timerange changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -318,7 +355,13 @@ describe('SearchBarComponent', () => {
|
|||
|
||||
it('calls useUpdateUrlParam when timeline timerange changes', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
@ -357,7 +400,13 @@ describe('SearchBarComponent', () => {
|
|||
|
||||
it('initializes timerange URL param with redux date on mount', async () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
jest.clearAllMocks();
|
||||
render(
|
||||
<TestProviders store={store}>
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { tableDefaults } from '../../store/data_table/defaults';
|
||||
import type { SubsetTGridModel } from '../../store/data_table/model';
|
||||
import type { ColumnHeaderOptions } from '../../../../common/types/timeline';
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
import type { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import {
|
||||
COLUMN_SESSION_START,
|
||||
COLUMN_EXECUTABLE,
|
||||
|
@ -63,11 +62,10 @@ export const sessionsHeaders: ColumnHeaderOptions[] = [
|
|||
export const getSessionsDefaultModel = (
|
||||
columns: ColumnHeaderOptions[],
|
||||
defaultColumns: ColumnHeaderOptions[]
|
||||
): SubsetTimelineModel => ({
|
||||
...timelineDefaults,
|
||||
): SubsetTGridModel => ({
|
||||
...tableDefaults,
|
||||
columns,
|
||||
defaultColumns,
|
||||
excludedRowRendererIds: Object.values(RowRendererId),
|
||||
sort: [
|
||||
{
|
||||
columnId: 'process.entry_leader.start',
|
||||
|
|
|
@ -10,10 +10,10 @@ import { waitFor, render } from '@testing-library/react';
|
|||
import { TestProviders } from '../../mock';
|
||||
import { TEST_ID, SessionsView, defaultSessionsFilter } from '.';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import { TimelineId } from '@kbn/timelines-plugin/common';
|
||||
import type { SessionsComponentsProps } from './types';
|
||||
import type { TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
import { TableId } from '../../../../common/types';
|
||||
import { licenseService } from '../../hooks/use_license';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
@ -34,7 +34,7 @@ const filterQuery =
|
|||
'{"bool":{"must":[],"filter":[{"match_phrase":{"host.name":{"query":"ubuntu-impish"}}}],"should":[],"must_not":[]}}';
|
||||
|
||||
const testProps: SessionsComponentsProps = {
|
||||
timelineId: TimelineId.hostsPageSessions,
|
||||
tableId: TableId.hostsPageSessions,
|
||||
entityType: 'sessions',
|
||||
pageFilters: [],
|
||||
startDate,
|
||||
|
@ -193,7 +193,7 @@ describe('SessionsView', () => {
|
|||
it('Action tab should have 5 columns when accessed via K8S dahsboard', async () => {
|
||||
render(
|
||||
<TestProviders>
|
||||
<SessionsView {...testProps} timelineId={TimelineId.kubernetesPageSessions} />
|
||||
<SessionsView {...testProps} tableId={TableId.kubernetesPageSessions} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import * as i18n from './translations';
|
|||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { useLicense } from '../../hooks/use_license';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TableId } from '../../../../common/types/timeline';
|
||||
export const TEST_ID = 'security_solution:sessions_viewer:sessions_view';
|
||||
|
||||
export const defaultSessionsFilter: Required<Pick<Filter, 'meta' | 'query'>> = {
|
||||
|
@ -59,7 +59,7 @@ export const defaultSessionsFilter: Required<Pick<Filter, 'meta' | 'query'>> = {
|
|||
};
|
||||
|
||||
const SessionsViewComponent: React.FC<SessionsComponentsProps> = ({
|
||||
timelineId,
|
||||
tableId,
|
||||
endDate,
|
||||
entityType = 'sessions',
|
||||
pageFilters,
|
||||
|
@ -93,7 +93,7 @@ const SessionsViewComponent: React.FC<SessionsComponentsProps> = ({
|
|||
);
|
||||
const isEnterprisePlus = useLicense().isEnterprise();
|
||||
const ACTION_BUTTON_COUNT =
|
||||
isEnterprisePlus || timelineId === TimelineId.kubernetesPageSessions ? 5 : 4;
|
||||
isEnterprisePlus || tableId === TableId.kubernetesPageSessions ? 5 : 4;
|
||||
const leadingControlColumns = useMemo(
|
||||
() => getDefaultControlColumn(ACTION_BUTTON_COUNT),
|
||||
[ACTION_BUTTON_COUNT]
|
||||
|
@ -109,7 +109,7 @@ const SessionsViewComponent: React.FC<SessionsComponentsProps> = ({
|
|||
defaultModel={getSessionsDefaultModel(columns, defaultColumns)}
|
||||
end={endDate}
|
||||
entityType={entityType}
|
||||
id={timelineId}
|
||||
tableId={tableId}
|
||||
leadingControlColumns={leadingControlColumns}
|
||||
renderCellValue={DefaultCellRenderer}
|
||||
rowRenderers={defaultRowRenderers}
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
import type { Filter } from '@kbn/es-query';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import type { QueryTabBodyProps } from '../../../hosts/pages/navigation/types';
|
||||
import type { TimelineIdLiteral, ColumnHeaderOptions } from '../../../../common/types/timeline';
|
||||
import type { ColumnHeaderOptions, TableIdLiteral } from '../../../../common/types/timeline';
|
||||
|
||||
export interface SessionsComponentsProps extends Pick<QueryTabBodyProps, 'endDate' | 'startDate'> {
|
||||
timelineId: TimelineIdLiteral;
|
||||
tableId: TableIdLiteral;
|
||||
pageFilters: Filter[];
|
||||
defaultFilters?: Filter[];
|
||||
entityType?: EntityType;
|
||||
|
|
|
@ -28,6 +28,7 @@ import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'
|
|||
import { TimelineId, TimelineType } from '../../../../common/types';
|
||||
import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants';
|
||||
import { sortWithExcludesAtEnd } from '../../../../common/utils/sourcerer';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
|
||||
|
@ -94,7 +95,13 @@ describe('Sourcerer component', () => {
|
|||
const pollForSignalIndexMock = jest.fn();
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
(useSourcererDataView as jest.Mock).mockReturnValue(sourcererDataView);
|
||||
(useSignalHelpers as jest.Mock).mockReturnValue({ signalIndexNeedsInit: false });
|
||||
});
|
||||
|
@ -206,6 +213,7 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -256,6 +264,7 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -307,7 +316,13 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
};
|
||||
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<Sourcerer {...defaultProps} />
|
||||
|
@ -352,7 +367,13 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
};
|
||||
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<Sourcerer scope={sourcererModel.SourcererScopeName.timeline} />
|
||||
|
@ -392,6 +413,7 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -449,6 +471,7 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -517,6 +540,7 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -561,7 +585,13 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
};
|
||||
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<Sourcerer scope={sourcererModel.SourcererScopeName.timeline} />
|
||||
|
@ -605,7 +635,13 @@ describe('Sourcerer component', () => {
|
|||
},
|
||||
};
|
||||
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<Sourcerer {...defaultProps} />
|
||||
|
@ -684,7 +720,13 @@ describe('Sourcerer component', () => {
|
|||
describe('sourcerer on alerts page or rules details page', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const testProps = {
|
||||
scope: sourcererModel.SourcererScopeName.detections,
|
||||
};
|
||||
|
@ -753,7 +795,13 @@ describe('sourcerer on alerts page or rules details page', () => {
|
|||
describe('timeline sourcerer', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const testProps = {
|
||||
scope: sourcererModel.SourcererScopeName.timeline,
|
||||
};
|
||||
|
@ -843,7 +891,13 @@ describe('timeline sourcerer', () => {
|
|||
},
|
||||
};
|
||||
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
@ -887,7 +941,13 @@ describe('Sourcerer integration tests', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
(useSourcererDataView as jest.Mock).mockReturnValue(sourcererDataView);
|
||||
store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
@ -932,7 +992,13 @@ describe('No data', () => {
|
|||
...sourcererDataView,
|
||||
indicesExist: false,
|
||||
});
|
||||
store = createStore(mockNoIndicesState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
mockNoIndicesState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
@ -1007,7 +1073,13 @@ describe('Update available', () => {
|
|||
...sourcererDataView,
|
||||
activePatterns: ['myFakebeat-*'],
|
||||
});
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
@ -1136,7 +1208,13 @@ describe('Update available for timeline template', () => {
|
|||
...sourcererDataView,
|
||||
activePatterns: ['myFakebeat-*'],
|
||||
});
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
@ -1221,7 +1299,13 @@ describe('Missing index patterns', () => {
|
|||
});
|
||||
const state3 = cloneDeep(state2);
|
||||
state3.timeline.timelineById[TimelineId.active].timelineType = TimelineType.default;
|
||||
store = createStore(state3, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state3,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
@ -1257,7 +1341,13 @@ describe('Missing index patterns', () => {
|
|||
...sourcererDataView,
|
||||
activePatterns: ['myFakebeat-*'],
|
||||
});
|
||||
store = createStore(state2, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state2,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
|
|
@ -43,6 +43,7 @@ import type {
|
|||
} from '../../../../common/search_strategy';
|
||||
import { getMockTheme } from '../../lib/kibana/kibana_react.mock';
|
||||
import * as module from '../../containers/query_toggle';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const from = '2019-06-15T06:00:00.000Z';
|
||||
const to = '2019-06-18T06:00:00.000Z';
|
||||
|
@ -65,7 +66,13 @@ describe('Stat Items Component', () => {
|
|||
const mockTheme = getMockTheme({ eui: { euiColorMediumShade: '#ece' } });
|
||||
const state: State = mockGlobalState;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const testProps = {
|
||||
description: 'HOSTS',
|
||||
fields: [{ key: 'hosts', value: null, color: '#6092C0', icon: 'cross' }],
|
||||
|
|
|
@ -24,6 +24,7 @@ import { createStore } from '../../store';
|
|||
import { SuperDatePicker, makeMapStateToProps } from '.';
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
const mockUseUiSetting$ = useUiSetting$ as jest.Mock;
|
||||
|
@ -84,11 +85,23 @@ describe('SIEM Super Date Picker', () => {
|
|||
describe('#SuperDatePicker', () => {
|
||||
const state: State = mockGlobalState;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
let store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
mockUseUiSetting$.mockImplementation((key, defaultValue) => {
|
||||
const useUiSetting$Mock = createUseUiSetting$Mock();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { Filter } from '@kbn/es-query';
|
||||
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TableId, TimelineId } from '../../../../common/types/timeline';
|
||||
import {
|
||||
alertEvents,
|
||||
allEvents,
|
||||
|
@ -22,18 +22,18 @@ import {
|
|||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
|
||||
/** the following `TimelineId`s are detection alert tables */
|
||||
const detectionAlertsTimelines = [TimelineId.detectionsPage, TimelineId.detectionsRulesDetailsPage];
|
||||
const detectionAlertsTimelines = [TableId.alertsOnAlertsPage, TableId.alertsOnRuleDetailsPage];
|
||||
|
||||
/** the following `TimelineId`s are NOT detection alert tables */
|
||||
const otherTimelines = [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.hostsPageSessions,
|
||||
TimelineId.networkPageEvents,
|
||||
TableId.hostsPageEvents,
|
||||
TableId.hostsPageSessions,
|
||||
TableId.networkPageEvents,
|
||||
TimelineId.active,
|
||||
TimelineId.casePage,
|
||||
TimelineId.test,
|
||||
TimelineId.alternateTest,
|
||||
TimelineId.kubernetesPageSessions,
|
||||
TableId.alternateTest,
|
||||
TableId.kubernetesPageSessions,
|
||||
];
|
||||
|
||||
const othersWithoutActive = otherTimelines.filter((x) => x !== TimelineId.active);
|
||||
|
@ -169,121 +169,103 @@ describe('getOptions', () => {
|
|||
});
|
||||
|
||||
describe('isDetectionsAlertsTable', () => {
|
||||
detectionAlertsTimelines.forEach((timelineId) =>
|
||||
test(`it returns true for detections alerts table '${timelineId}'`, () => {
|
||||
expect(isDetectionsAlertsTable(timelineId)).toEqual(true);
|
||||
detectionAlertsTimelines.forEach((tableId) =>
|
||||
test(`it returns true for detections alerts table '${tableId}'`, () => {
|
||||
expect(isDetectionsAlertsTable(tableId)).toEqual(true);
|
||||
})
|
||||
);
|
||||
|
||||
otherTimelines.forEach((timelineId) =>
|
||||
test(`it returns false for (NON alert table) timeline '${timelineId}'`, () => {
|
||||
expect(isDetectionsAlertsTable(timelineId)).toEqual(false);
|
||||
otherTimelines.forEach((tableId) =>
|
||||
test(`it returns false for (NON alert table) timeline '${tableId}'`, () => {
|
||||
expect(isDetectionsAlertsTable(tableId)).toEqual(false);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('shouldIgnoreAlertFilters', () => {
|
||||
detectionAlertsTimelines.forEach((timelineId) => {
|
||||
test(`it returns true when the view is 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
detectionAlertsTimelines.forEach((tableId) => {
|
||||
test(`it returns true when the view is 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(shouldIgnoreAlertFilters({ timelineId, view })).toEqual(true);
|
||||
expect(shouldIgnoreAlertFilters({ tableId, view })).toEqual(true);
|
||||
});
|
||||
|
||||
test(`it returns false when the view is NOT 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
test(`it returns false when the view is NOT 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'alert'; // the default selection for detection alert tables
|
||||
expect(shouldIgnoreAlertFilters({ timelineId, view })).toEqual(false);
|
||||
expect(shouldIgnoreAlertFilters({ tableId, view })).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
otherTimelines.forEach((timelineId) => {
|
||||
test(`it returns false when the view is 'raw' for (NON alert table) timeline'${timelineId}'`, () => {
|
||||
otherTimelines.forEach((tableId) => {
|
||||
test(`it returns false when the view is 'raw' for (NON alert table) timeline'${tableId}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(shouldIgnoreAlertFilters({ timelineId, view })).toEqual(false);
|
||||
expect(shouldIgnoreAlertFilters({ tableId, view })).toEqual(false);
|
||||
});
|
||||
|
||||
test(`it returns false when the view is NOT 'raw' for (NON alert table) timeline '${timelineId}'`, () => {
|
||||
test(`it returns false when the view is NOT 'raw' for (NON alert table) timeline '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(shouldIgnoreAlertFilters({ timelineId, view })).toEqual(false);
|
||||
expect(shouldIgnoreAlertFilters({ tableId, view })).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeIgnoredAlertFilters', () => {
|
||||
detectionAlertsTimelines.forEach((timelineId) => {
|
||||
test(`it removes the ignored alert filters when the view is 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
detectionAlertsTimelines.forEach((tableId) => {
|
||||
test(`it removes the ignored alert filters when the view is 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, timelineId, view })).toEqual([
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, tableId, view })).toEqual([
|
||||
hostNameFilter,
|
||||
]);
|
||||
});
|
||||
|
||||
test(`it does NOT remove any filters when the view is NOT 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
test(`it does NOT remove any filters when the view is NOT 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, timelineId, view })).toEqual(
|
||||
allFilters
|
||||
);
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, tableId, view })).toEqual(allFilters);
|
||||
});
|
||||
});
|
||||
|
||||
otherTimelines.forEach((timelineId) => {
|
||||
test(`it does NOT remove any filters when the view is 'raw' for (NON alert table) '${timelineId}'`, () => {
|
||||
otherTimelines.forEach((tableId) => {
|
||||
test(`it does NOT remove any filters when the view is 'raw' for (NON alert table) '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, timelineId, view })).toEqual(
|
||||
allFilters
|
||||
);
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, tableId, view })).toEqual(allFilters);
|
||||
});
|
||||
|
||||
test(`it does NOT remove any filters when the view is NOT 'raw' for (NON alert table '${timelineId}'`, () => {
|
||||
test(`it does NOT remove any filters when the view is NOT 'raw' for (NON alert table '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, timelineId, view })).toEqual(
|
||||
allFilters
|
||||
);
|
||||
expect(removeIgnoredAlertFilters({ filters: allFilters, tableId, view })).toEqual(allFilters);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSourcererScopeName', () => {
|
||||
detectionAlertsTimelines.forEach((timelineId) => {
|
||||
test(`it returns the 'default' SourcererScopeName when the view is 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
detectionAlertsTimelines.forEach((tableId) => {
|
||||
test(`it returns the 'default' SourcererScopeName when the view is 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(getSourcererScopeName({ timelineId, view })).toEqual(SourcererScopeName.default);
|
||||
expect(getSourcererScopeName({ scopeId: tableId, view })).toEqual(SourcererScopeName.default);
|
||||
});
|
||||
|
||||
test(`it returns the 'detections' SourcererScopeName when the view is NOT 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
test(`it returns the 'detections' SourcererScopeName when the view is NOT 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(getSourcererScopeName({ timelineId, view })).toEqual(SourcererScopeName.detections);
|
||||
expect(getSourcererScopeName({ scopeId: tableId, view })).toEqual(
|
||||
SourcererScopeName.detections
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test(`it returns the 'default' SourcererScopeName when timelineId is undefined'`, () => {
|
||||
const timelineId = undefined;
|
||||
const tableId = undefined;
|
||||
const view = 'raw';
|
||||
expect(getSourcererScopeName({ timelineId, view })).toEqual(SourcererScopeName.default);
|
||||
expect(getSourcererScopeName({ scopeId: tableId, view })).toEqual(SourcererScopeName.default);
|
||||
});
|
||||
|
||||
test(`it returns the 'timeline' SourcererScopeName when the view is 'raw' for the active timeline '${TimelineId.active}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(getSourcererScopeName({ timelineId: TimelineId.active, view })).toEqual(
|
||||
SourcererScopeName.timeline
|
||||
);
|
||||
});
|
||||
|
||||
test(`it returns the 'timeline' SourcererScopeName when the view is NOT 'raw' for the active timeline '${TimelineId.active}'`, () => {
|
||||
const view = 'all';
|
||||
expect(getSourcererScopeName({ timelineId: TimelineId.active, view })).toEqual(
|
||||
SourcererScopeName.timeline
|
||||
);
|
||||
});
|
||||
|
||||
othersWithoutActive.forEach((timelineId) => {
|
||||
test(`it returns the 'default' SourcererScopeName when the view is 'raw' for (NON alert table) timeline '${timelineId}'`, () => {
|
||||
othersWithoutActive.forEach((tableId) => {
|
||||
test(`it returns the 'default' SourcererScopeName when the view is 'raw' for (NON alert table) timeline '${tableId}'`, () => {
|
||||
const view = 'raw';
|
||||
expect(getSourcererScopeName({ timelineId, view })).toEqual(SourcererScopeName.default);
|
||||
expect(getSourcererScopeName({ scopeId: tableId, view })).toEqual(SourcererScopeName.default);
|
||||
});
|
||||
|
||||
test(`it returns the 'default' SourcererScopeName when the view is NOT 'raw' for detections alerts table '${timelineId}'`, () => {
|
||||
test(`it returns the 'default' SourcererScopeName when the view is NOT 'raw' for detections alerts table '${tableId}'`, () => {
|
||||
const view = 'alert';
|
||||
expect(getSourcererScopeName({ timelineId, view })).toEqual(SourcererScopeName.default);
|
||||
expect(getSourcererScopeName({ scopeId: tableId, view })).toEqual(SourcererScopeName.default);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
|
||||
import type { TimelineEventsType } from '../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TimelineId, TableId } from '../../../../common/types/timeline';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
@ -117,8 +117,8 @@ export const getOptions = (activeTimelineEventsType?: TimelineEventsType): TopNO
|
|||
};
|
||||
|
||||
/** returns true if the specified timelineId is a detections alert table */
|
||||
export const isDetectionsAlertsTable = (timelineId: string | undefined): boolean =>
|
||||
timelineId === TimelineId.detectionsPage || timelineId === TimelineId.detectionsRulesDetailsPage;
|
||||
export const isDetectionsAlertsTable = (tableId: string | undefined): boolean =>
|
||||
tableId === TableId.alertsOnAlertsPage || tableId === TableId.alertsOnRuleDetailsPage;
|
||||
|
||||
/**
|
||||
* The following fields are used to filter alerts tables, (i.e. tables in the
|
||||
|
@ -185,12 +185,12 @@ export const IGNORED_ALERT_FILTERS = [
|
|||
* @see IGNORED_ALERT_FILTERS
|
||||
*/
|
||||
export const shouldIgnoreAlertFilters = ({
|
||||
timelineId,
|
||||
tableId,
|
||||
view,
|
||||
}: {
|
||||
timelineId: string | undefined;
|
||||
tableId: string | undefined;
|
||||
view: TimelineEventsType;
|
||||
}): boolean => view === 'raw' && isDetectionsAlertsTable(timelineId);
|
||||
}): boolean => view === 'raw' && isDetectionsAlertsTable(tableId);
|
||||
|
||||
/**
|
||||
* returns a new set of `filters` that don't contain the fields specified in
|
||||
|
@ -200,14 +200,14 @@ export const shouldIgnoreAlertFilters = ({
|
|||
*/
|
||||
export const removeIgnoredAlertFilters = ({
|
||||
filters,
|
||||
timelineId,
|
||||
tableId,
|
||||
view,
|
||||
}: {
|
||||
filters: Filter[];
|
||||
timelineId: string | undefined;
|
||||
tableId: string | undefined;
|
||||
view: TimelineEventsType;
|
||||
}): Filter[] => {
|
||||
if (!shouldIgnoreAlertFilters({ timelineId, view })) {
|
||||
if (!shouldIgnoreAlertFilters({ tableId, view })) {
|
||||
return filters; // unmodified filters
|
||||
}
|
||||
|
||||
|
@ -216,23 +216,23 @@ export const removeIgnoredAlertFilters = ({
|
|||
|
||||
/** returns the SourcererScopeName applicable to the specified timelineId and view */
|
||||
export const getSourcererScopeName = ({
|
||||
timelineId,
|
||||
scopeId,
|
||||
view,
|
||||
}: {
|
||||
timelineId: string | undefined;
|
||||
scopeId: string | undefined;
|
||||
view: TimelineEventsType;
|
||||
}): SourcererScopeName => {
|
||||
// When alerts should be ignored, use the `default` Sourcerer scope,
|
||||
// because it does NOT include alert indexes:
|
||||
if (shouldIgnoreAlertFilters({ timelineId, view })) {
|
||||
if (shouldIgnoreAlertFilters({ tableId: scopeId, view })) {
|
||||
return SourcererScopeName.default; // no alerts in this scope
|
||||
}
|
||||
|
||||
if (isDetectionsAlertsTable(timelineId)) {
|
||||
if (isDetectionsAlertsTable(scopeId)) {
|
||||
return SourcererScopeName.detections;
|
||||
}
|
||||
|
||||
if (timelineId === TimelineId.active) {
|
||||
if (scopeId === TimelineId.active) {
|
||||
return SourcererScopeName.timeline;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,8 @@ import { createStore } from '../../store';
|
|||
|
||||
import type { Props } from './top_n';
|
||||
import { StatefulTopN } from '.';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TableId, TimelineId } from '../../../../common/types/timeline';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
|
@ -93,7 +94,7 @@ const state: State = {
|
|||
...mockGlobalState.timeline,
|
||||
timelineById: {
|
||||
[TimelineId.active]: {
|
||||
...mockGlobalState.timeline.timelineById.test,
|
||||
...mockGlobalState.timeline.timelineById['timeline-test'],
|
||||
id: TimelineId.active,
|
||||
dataProviders: [
|
||||
{
|
||||
|
@ -149,13 +150,19 @@ const state: State = {
|
|||
};
|
||||
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
let testProps = {
|
||||
browserFields: mockBrowserFields,
|
||||
field,
|
||||
indexPattern: mockIndexPattern,
|
||||
timelineId: TimelineId.hostsPageEvents,
|
||||
scopeId: TableId.hostsPageEvents,
|
||||
toggleTopN: jest.fn(),
|
||||
onFilterAdded: jest.fn(),
|
||||
value,
|
||||
|
@ -292,13 +299,14 @@ describe('StatefulTopN', () => {
|
|||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
testProps = {
|
||||
...testProps,
|
||||
timelineId: TimelineId.active,
|
||||
};
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<StatefulTopN {...testProps} />
|
||||
<StatefulTopN
|
||||
{...{
|
||||
...testProps,
|
||||
scopeId: TimelineId.active,
|
||||
}}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
@ -357,7 +365,7 @@ describe('StatefulTopN', () => {
|
|||
test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, async () => {
|
||||
testProps = {
|
||||
...testProps,
|
||||
timelineId: TimelineId.detectionsPage,
|
||||
scopeId: TableId.alertsOnAlertsPage,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
|
|
|
@ -11,6 +11,7 @@ import { connect } from 'react-redux';
|
|||
|
||||
import type { DataViewBase, Filter, Query } from '@kbn/es-query';
|
||||
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
||||
import { isActiveTimeline } from '../../../helpers';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { useGlobalTime } from '../../containers/use_global_time';
|
||||
import type { BrowserFields } from '../../containers/source';
|
||||
|
@ -22,7 +23,7 @@ import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
|||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import type { TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
|
||||
import { getOptions } from './helpers';
|
||||
import { getOptions, isDetectionsAlertsTable } from './helpers';
|
||||
import { TopN } from './top_n';
|
||||
import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import type { AlertsStackByField } from '../../../detections/components/alerts_kpis/common/types';
|
||||
|
@ -77,7 +78,7 @@ export interface OwnProps {
|
|||
browserFields: BrowserFields;
|
||||
field: string;
|
||||
indexPattern: DataViewBase;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
toggleTopN: () => void;
|
||||
onFilterAdded?: () => void;
|
||||
paddingSize?: 's' | 'm' | 'l' | 'none';
|
||||
|
@ -104,20 +105,18 @@ const StatefulTopNComponent: React.FC<Props> = ({
|
|||
onFilterAdded,
|
||||
paddingSize,
|
||||
showLegend,
|
||||
timelineId,
|
||||
scopeId,
|
||||
toggleTopN,
|
||||
value,
|
||||
}) => {
|
||||
const { uiSettings } = useKibana().services;
|
||||
const { from, deleteQuery, setQuery, to } = useGlobalTime(false);
|
||||
|
||||
const options = getOptions(
|
||||
timelineId === TimelineId.active ? activeTimelineEventType : undefined
|
||||
);
|
||||
const options = getOptions(isActiveTimeline(scopeId ?? '') ? activeTimelineEventType : undefined);
|
||||
|
||||
const combinedQueries = useMemo(
|
||||
() =>
|
||||
timelineId === TimelineId.active
|
||||
isActiveTimeline(scopeId ?? '')
|
||||
? combineQueries({
|
||||
browserFields,
|
||||
config: getEsQueryConfig(uiSettings),
|
||||
|
@ -132,24 +131,20 @@ const StatefulTopNComponent: React.FC<Props> = ({
|
|||
})?.filterQuery
|
||||
: undefined,
|
||||
[
|
||||
activeTimelineFilters,
|
||||
activeTimelineKqlQueryExpression,
|
||||
scopeId,
|
||||
browserFields,
|
||||
uiSettings,
|
||||
dataProviders,
|
||||
activeTimelineFilters,
|
||||
indexPattern,
|
||||
kqlMode,
|
||||
timelineId,
|
||||
uiSettings,
|
||||
activeTimelineKqlQueryExpression,
|
||||
]
|
||||
);
|
||||
|
||||
const defaultView = useMemo(
|
||||
() =>
|
||||
timelineId === TimelineId.detectionsPage ||
|
||||
timelineId === TimelineId.detectionsRulesDetailsPage
|
||||
? 'alert'
|
||||
: options[0].value,
|
||||
[options, timelineId]
|
||||
() => (isDetectionsAlertsTable(scopeId) ? 'alert' : options[0].value),
|
||||
[options, scopeId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -157,21 +152,21 @@ const StatefulTopNComponent: React.FC<Props> = ({
|
|||
combinedQueries={combinedQueries}
|
||||
data-test-subj="top-n"
|
||||
defaultView={defaultView}
|
||||
deleteQuery={timelineId === TimelineId.active ? undefined : deleteQuery}
|
||||
deleteQuery={isActiveTimeline(scopeId ?? '') ? undefined : deleteQuery}
|
||||
field={field as AlertsStackByField}
|
||||
filters={timelineId === TimelineId.active ? EMPTY_FILTERS : globalFilters}
|
||||
from={timelineId === TimelineId.active ? activeTimelineFrom : from}
|
||||
filters={isActiveTimeline(scopeId ?? '') ? EMPTY_FILTERS : globalFilters}
|
||||
from={isActiveTimeline(scopeId ?? '') ? activeTimelineFrom : from}
|
||||
indexPattern={indexPattern}
|
||||
options={options}
|
||||
paddingSize={paddingSize}
|
||||
query={timelineId === TimelineId.active ? EMPTY_QUERY : globalQuery}
|
||||
query={isActiveTimeline(scopeId ?? '') ? EMPTY_QUERY : globalQuery}
|
||||
showLegend={showLegend}
|
||||
setAbsoluteRangeDatePickerTarget={
|
||||
timelineId === TimelineId.active ? InputsModelId.timeline : InputsModelId.global
|
||||
isActiveTimeline(scopeId ?? '') ? InputsModelId.timeline : InputsModelId.global
|
||||
}
|
||||
setQuery={setQuery}
|
||||
timelineId={timelineId}
|
||||
to={timelineId === TimelineId.active ? activeTimelineTo : to}
|
||||
scopeId={scopeId}
|
||||
to={isActiveTimeline(scopeId ?? '') ? activeTimelineTo : to}
|
||||
toggleTopN={toggleTopN}
|
||||
onFilterAdded={onFilterAdded}
|
||||
value={value}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { mount } from 'enzyme';
|
|||
import React from 'react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { TableId, TimelineId } from '../../../../common/types';
|
||||
import '../../mock/match_media';
|
||||
import { TestProviders, mockIndexPattern } from '../../mock';
|
||||
|
||||
|
@ -137,21 +137,18 @@ describe('TopN', () => {
|
|||
});
|
||||
|
||||
describe('view selection', () => {
|
||||
const detectionAlertsTimelines = [
|
||||
TimelineId.detectionsPage,
|
||||
TimelineId.detectionsRulesDetailsPage,
|
||||
];
|
||||
const detectionAlertsTimelines = [TableId.alertsOnAlertsPage, TableId.alertsOnRuleDetailsPage];
|
||||
|
||||
const nonDetectionAlertTables = [
|
||||
TimelineId.hostsPageEvents,
|
||||
TimelineId.networkPageEvents,
|
||||
TableId.hostsPageEvents,
|
||||
TableId.networkPageEvents,
|
||||
TimelineId.casePage,
|
||||
];
|
||||
|
||||
test('it disables view selection when timelineId is undefined', () => {
|
||||
test('it disables view selection when scopeId is undefined', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<TopN {...testProps} timelineId={undefined} />
|
||||
<TopN {...testProps} scopeId={undefined} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="view-select"]').first().props().disabled).toBe(true);
|
||||
|
@ -160,28 +157,28 @@ describe('TopN', () => {
|
|||
test('it disables view selection when timelineId is `active`', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<TopN {...testProps} timelineId={TimelineId.active} />
|
||||
<TopN {...testProps} scopeId={TimelineId.active} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="view-select"]').first().props().disabled).toBe(true);
|
||||
});
|
||||
|
||||
detectionAlertsTimelines.forEach((timelineId) => {
|
||||
test(`it enables view selection for detection alert table '${timelineId}'`, () => {
|
||||
detectionAlertsTimelines.forEach((tableId) => {
|
||||
test(`it enables view selection for detection alert table '${tableId}'`, () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<TopN {...testProps} timelineId={timelineId} />
|
||||
<TopN {...testProps} scopeId={tableId} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="view-select"]').first().props().disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
nonDetectionAlertTables.forEach((timelineId) => {
|
||||
test(`it disables view selection for NON detection alert table '${timelineId}'`, () => {
|
||||
nonDetectionAlertTables.forEach((tableId) => {
|
||||
test(`it disables view selection for NON detection alert table '${tableId}'`, () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<TopN {...testProps} timelineId={timelineId} />
|
||||
<TopN {...testProps} scopeId={tableId} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="view-select"]').first().props().disabled).toBe(true);
|
||||
|
|
|
@ -60,7 +60,7 @@ export interface Props extends Pick<GlobalTimeArgs, 'from' | 'to' | 'deleteQuery
|
|||
query: Query;
|
||||
setAbsoluteRangeDatePickerTarget: InputsModelId;
|
||||
showLegend?: boolean;
|
||||
timelineId?: string;
|
||||
scopeId?: string;
|
||||
toggleTopN: () => void;
|
||||
onFilterAdded?: () => void;
|
||||
value?: string[] | string | null;
|
||||
|
@ -80,7 +80,7 @@ const TopNComponent: React.FC<Props> = ({
|
|||
showLegend,
|
||||
setAbsoluteRangeDatePickerTarget,
|
||||
setQuery,
|
||||
timelineId,
|
||||
scopeId,
|
||||
to,
|
||||
toggleTopN,
|
||||
}) => {
|
||||
|
@ -90,7 +90,7 @@ const TopNComponent: React.FC<Props> = ({
|
|||
[setView]
|
||||
);
|
||||
const { selectedPatterns, runtimeMappings } = useSourcererDataView(
|
||||
getSourcererScopeName({ timelineId, view })
|
||||
getSourcererScopeName({ scopeId, view })
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -101,20 +101,20 @@ const TopNComponent: React.FC<Props> = ({
|
|||
() => (
|
||||
<ViewSelect
|
||||
data-test-subj="view-select"
|
||||
disabled={!isDetectionsAlertsTable(timelineId)}
|
||||
disabled={!isDetectionsAlertsTable(scopeId)}
|
||||
onChange={onViewSelected}
|
||||
options={options}
|
||||
valueOfSelected={view}
|
||||
/>
|
||||
),
|
||||
[onViewSelected, options, timelineId, view]
|
||||
[onViewSelected, options, scopeId, view]
|
||||
);
|
||||
|
||||
// alert workflow statuses (e.g. open | closed) and other alert-specific
|
||||
// filters must be ignored when viewing raw alerts
|
||||
const applicableFilters = useMemo(
|
||||
() => removeIgnoredAlertFilters({ filters, timelineId, view }),
|
||||
[filters, timelineId, view]
|
||||
() => removeIgnoredAlertFilters({ filters, tableId: scopeId, view }),
|
||||
[filters, scopeId, view]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -146,7 +146,7 @@ const TopNComponent: React.FC<Props> = ({
|
|||
setQuery={setQuery}
|
||||
showSpacer={false}
|
||||
toggleTopN={toggleTopN}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
to={to}
|
||||
/>
|
||||
) : (
|
||||
|
@ -159,7 +159,6 @@ const TopNComponent: React.FC<Props> = ({
|
|||
query={query}
|
||||
showLegend={showLegend}
|
||||
setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget}
|
||||
timelineId={timelineId}
|
||||
runtimeMappings={runtimeMappings}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { CASES_FEATURE_ID } from '../../../../common/constants';
|
|||
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
|
||||
import { allCasesCapabilities, allCasesPermissions } from '../../../cases_test_utils';
|
||||
import { InputsModelId } from '../../store/inputs/constants';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
jest.mock('react-router-dom', () => {
|
||||
const actual = jest.requireActual('react-router-dom');
|
||||
return {
|
||||
|
@ -56,7 +57,13 @@ describe('VisualizationActions', () => {
|
|||
state: state.inputs,
|
||||
};
|
||||
|
||||
let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
let store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const props = {
|
||||
lensAttributes: dnsTopDomainsLensAttributes,
|
||||
queryId: 'networkDnsHistogramQuery',
|
||||
|
@ -117,7 +124,13 @@ describe('VisualizationActions', () => {
|
|||
});
|
||||
const myState = cloneDeep(state);
|
||||
myState.inputs = upsertQuery(newQuery);
|
||||
store = createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
});
|
||||
|
||||
test('Should render VisualizationActions button', () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import React from 'react';
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
import {
|
||||
TestProviders,
|
||||
mockGlobalState,
|
||||
|
@ -57,7 +58,13 @@ export const mockCreateStoreWithQueryFilters = () => {
|
|||
filters: filterFromSearchBar,
|
||||
},
|
||||
};
|
||||
return createStore(myState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
return createStore(
|
||||
myState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
};
|
||||
|
||||
export const wrapper = ({ children }: { children: React.ReactElement }) => (
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('useAlertPrevalence', () => {
|
|||
useAlertPrevalence({
|
||||
field: 'host.name',
|
||||
value: ['Host-byc3w6qlpo'],
|
||||
timelineId: 'detections-page',
|
||||
isActiveTimelines: false,
|
||||
signalIndexName: null,
|
||||
includeAlertIds: false,
|
||||
}),
|
||||
|
|
|
@ -12,7 +12,6 @@ import { useGlobalTime } from '../use_global_time';
|
|||
import type { GenericBuckets } from '../../../../common/search_strategy';
|
||||
import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query';
|
||||
import { ALERTS_QUERY_NAMES } from '../../../detections/containers/detection_engine/alerts/constants';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { inputsSelectors } from '../../store';
|
||||
|
||||
|
@ -21,7 +20,7 @@ const ALERT_PREVALENCE_AGG = 'countOfAlertsWithSameFieldAndValue';
|
|||
interface UseAlertPrevalenceOptions {
|
||||
field: string;
|
||||
value: string | string[] | undefined | null;
|
||||
timelineId: string;
|
||||
isActiveTimelines: boolean;
|
||||
signalIndexName: string | null;
|
||||
includeAlertIds?: boolean;
|
||||
ignoreTimerange?: boolean;
|
||||
|
@ -37,7 +36,7 @@ interface UserAlertPrevalenceResult {
|
|||
export const useAlertPrevalence = ({
|
||||
field,
|
||||
value,
|
||||
timelineId,
|
||||
isActiveTimelines,
|
||||
signalIndexName,
|
||||
includeAlertIds = false,
|
||||
ignoreTimerange = false,
|
||||
|
@ -49,7 +48,7 @@ export const useAlertPrevalence = ({
|
|||
let to: string | undefined;
|
||||
let from: string | undefined;
|
||||
if (ignoreTimerange === false) {
|
||||
({ to, from } = timelineId === TimelineId.active ? timelineTime : globalTime);
|
||||
({ to, from } = isActiveTimelines ? timelineTime : globalTime);
|
||||
}
|
||||
const [initialQuery] = useState(() =>
|
||||
generateAlertPrevalenceQuery(field, value, from, to, includeAlertIds)
|
||||
|
|
|
@ -30,7 +30,7 @@ interface EntityResponse {
|
|||
interface UseAlertPrevalenceFromProcessTree {
|
||||
processEntityId: string;
|
||||
documentId: string;
|
||||
timelineId: string;
|
||||
isActiveTimeline: boolean;
|
||||
indices: string[];
|
||||
}
|
||||
|
||||
|
@ -94,12 +94,12 @@ function useAlertDocumentAnalyzerSchema({ documentId, indices }: UseAlertDocumen
|
|||
export function useAlertPrevalenceFromProcessTree({
|
||||
processEntityId,
|
||||
documentId,
|
||||
timelineId,
|
||||
isActiveTimeline,
|
||||
indices,
|
||||
}: UseAlertPrevalenceFromProcessTree): UserAlertPrevalenceFromProcessTreeResult {
|
||||
const http = useHttp();
|
||||
|
||||
const { selectedPatterns } = useTimelineDataFilters(timelineId);
|
||||
const { selectedPatterns } = useTimelineDataFilters(isActiveTimeline);
|
||||
const alertAndOriginalIndices = [...new Set(selectedPatterns.concat(indices))];
|
||||
const { loading, id, schema } = useAlertDocumentAnalyzerSchema({
|
||||
documentId,
|
||||
|
|
|
@ -37,6 +37,7 @@ import { postSourcererDataView } from './api';
|
|||
import * as source from '../source/use_data_view';
|
||||
import { sourcererActions } from '../../store/sourcerer';
|
||||
import { useInitializeUrlParam, useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
const mockRouteSpy: RouteSpyState = {
|
||||
pageName: SecurityPageName.overview,
|
||||
|
@ -111,7 +112,13 @@ describe('Sourcerer Hooks', () => {
|
|||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.restoreAllMocks();
|
||||
store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
store = createStore(
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
mockUseUserInfo.mockImplementation(() => userInfoState);
|
||||
});
|
||||
it('initializes loading default and timeline index patterns', async () => {
|
||||
|
@ -157,6 +164,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -259,6 +267,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -329,6 +338,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -387,6 +397,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -428,6 +439,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -470,6 +482,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -516,6 +529,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
@ -564,6 +578,7 @@ describe('Sourcerer Hooks', () => {
|
|||
},
|
||||
},
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
|
|
@ -17,6 +17,7 @@ import { act, renderHook } from '@testing-library/react-hooks';
|
|||
import { useSignalHelpers } from './use_signal_helpers';
|
||||
import type { State } from '../../store';
|
||||
import { createStore } from '../../store';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
describe('useSignalHelpers', () => {
|
||||
const wrapperContainer: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
|
||||
|
@ -53,7 +54,13 @@ describe('useSignalHelpers', () => {
|
|||
},
|
||||
};
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSignalHelpers(), {
|
||||
wrapper: ({ children }) => <TestProviders store={store}>{children}</TestProviders>,
|
||||
|
@ -83,7 +90,13 @@ describe('useSignalHelpers', () => {
|
|||
},
|
||||
};
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
|
||||
const store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSignalHelpers(), {
|
||||
wrapper: ({ children }) => <TestProviders store={store}>{children}</TestProviders>,
|
||||
|
|
|
@ -107,6 +107,7 @@ export const getAddToTimelineCellAction = ({
|
|||
ownFocus: false,
|
||||
showTooltip: false,
|
||||
onClick: handleAddToTimelineAction,
|
||||
timelineType: 'default',
|
||||
};
|
||||
}, [Component, columnId, dataProvider, handleAddToTimelineAction]);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import type { EuiDataGridColumn } from '@elastic/eui';
|
||||
import { TableId } from '../../../../common/types';
|
||||
import type {
|
||||
BrowserFields,
|
||||
TimelineNonEcsData,
|
||||
|
@ -20,7 +21,7 @@ describe('default cell actions', () => {
|
|||
const browserFields: BrowserFields = {};
|
||||
const data: TimelineNonEcsData[][] = [[]];
|
||||
const ecsData: Ecs[] = [];
|
||||
const timelineId = 'mockTimelineId';
|
||||
const tableId = TableId.test;
|
||||
const pageSize = 10;
|
||||
|
||||
test('columns without any link action (e.g.: signal.status) should return an empty component (not null or data grid would crash)', () => {
|
||||
|
@ -43,7 +44,7 @@ describe('default cell actions', () => {
|
|||
ecsData,
|
||||
header: columnHeaders.find((h) => h.id === header.id),
|
||||
pageSize,
|
||||
timelineId,
|
||||
scopeId: tableId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -75,7 +76,7 @@ describe('default cell actions', () => {
|
|||
ecsData,
|
||||
header: [columnHeaders].find((h) => h.id === header.id),
|
||||
pageSize,
|
||||
timelineId,
|
||||
scopeId: tableId,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('ExpandedCellValueActions', () => {
|
|||
},
|
||||
globalFilters: [],
|
||||
onFilterAdded: () => {},
|
||||
timelineId: 'mockTimelineId',
|
||||
scopeId: 'mockTimelineId',
|
||||
value: ['mock value'],
|
||||
};
|
||||
const wrapper = shallow(<ExpandedCellValueActions {...props} />);
|
||||
|
|
|
@ -19,7 +19,7 @@ import { SHOW_TOP_VALUES, HIDE_TOP_VALUES } from './translations';
|
|||
interface Props {
|
||||
field: ColumnHeaderOptions;
|
||||
globalFilters?: Filter[];
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
value: string[] | undefined;
|
||||
onFilterAdded?: () => void;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ const ExpandedCellValueActionsComponent: React.FC<Props> = ({
|
|||
field,
|
||||
globalFilters,
|
||||
onFilterAdded,
|
||||
timelineId,
|
||||
scopeId,
|
||||
value,
|
||||
}) => {
|
||||
const {
|
||||
|
@ -82,7 +82,7 @@ const ExpandedCellValueActionsComponent: React.FC<Props> = ({
|
|||
showLegend
|
||||
showTopN={showTopN}
|
||||
showTooltip={false}
|
||||
timelineId={timelineId}
|
||||
scopeId={scopeId}
|
||||
title={showTopN ? HIDE_TOP_VALUES : SHOW_TOP_VALUES}
|
||||
value={value}
|
||||
/>
|
||||
|
|
|
@ -92,14 +92,14 @@ export const FieldValueCell = ({
|
|||
data,
|
||||
ecsData,
|
||||
header,
|
||||
timelineId,
|
||||
scopeId,
|
||||
pageSize,
|
||||
closeCellPopover,
|
||||
}: {
|
||||
data: TimelineNonEcsData[][];
|
||||
ecsData: Ecs[];
|
||||
header?: ColumnHeaderOptions;
|
||||
timelineId: string;
|
||||
scopeId: string;
|
||||
pageSize: number;
|
||||
closeCellPopover?: () => void;
|
||||
}) => {
|
||||
|
@ -134,7 +134,7 @@ export const FieldValueCell = ({
|
|||
return showEmpty === false ? (
|
||||
<FormattedFieldValue
|
||||
Component={Component}
|
||||
contextId={`expanded-value-${columnId}-row-${pageRowIndex}-${timelineId}`}
|
||||
contextId={`expanded-value-${columnId}-row-${pageRowIndex}-${scopeId}`}
|
||||
eventId={eventId}
|
||||
fieldFormat={fieldFormat}
|
||||
isAggregatable={header.aggregatable ?? false}
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* 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 { scaleLog } from 'd3-scale';
|
||||
|
||||
/**
|
||||
* Polyfill is from: https://developers.google.com/web/updates/2015/08/using-requestidlecallback
|
||||
* This is for Safari 12.1.2 and IE-11
|
||||
*/
|
||||
// do not delete
|
||||
export const polyFillRequestIdleCallback = (callback: IdleRequestCallback) => {
|
||||
const start = Date.now();
|
||||
return setTimeout(() => {
|
||||
callback({
|
||||
didTimeout: false,
|
||||
timeRemaining: () => {
|
||||
return Math.max(0, 50 - (Date.now() - start));
|
||||
},
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a polyfill for "requestIdleCallback" but since it is an
|
||||
* experimental web API and TypeScript does not even support the API
|
||||
* properly, I left it as is within this file as a utility for us to change
|
||||
* tune as needed instead of pushing the experimental API to all users
|
||||
* of Kibana.
|
||||
*
|
||||
* NOTE: This might become obsolete once React releases its own
|
||||
* scheduler with fibers (Concurrent React) and we would then remove
|
||||
* this and all usages. Otherwise, just remove this note
|
||||
*/
|
||||
// do not delete
|
||||
export const requestIdleCallbackViaScheduler = (
|
||||
callback: IdleRequestCallback,
|
||||
opts?: IdleRequestOptions
|
||||
) => {
|
||||
if ('requestIdleCallback' in window) {
|
||||
window.requestIdleCallback(callback, opts);
|
||||
} else {
|
||||
polyFillRequestIdleCallback(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this with any maxDelay such as
|
||||
*
|
||||
* data.map((event, i) => {
|
||||
* const maxDelay = maxDelay(i);
|
||||
* }
|
||||
*
|
||||
* where i is within a loop of elements. You can start at 0 and go to Infinity and it
|
||||
* will clamp you at 100 milliseconds through 2000 milliseconds (2 seconds)
|
||||
* to delay a render.
|
||||
*
|
||||
* This function guarantees that your first element gets a chance to run up to
|
||||
* 100 milliseconds through the range of .range([100, 2000]).
|
||||
*
|
||||
* NOTE: ScaleLog cannot be given a zero, so the domain starting at 1
|
||||
* like so: domain([1, 100]) is intentional.
|
||||
*
|
||||
* NOTE: If you go above 25 elements this will at most return 2000 milliseconds for a
|
||||
* delayMax setting value meaning at most beyond 25 elements to display, they will take at most
|
||||
* 2 seconds to delay before show up.
|
||||
*/
|
||||
// do not delete
|
||||
export const maxDelay = scaleLog().domain([1, 25]).range([100, 2000]).clamp(true);
|
|
@ -12,8 +12,8 @@ import type {
|
|||
} from '@kbn/triggers-actions-ui-plugin/public';
|
||||
|
||||
import { APP_ID, CASES_FEATURE_ID } from '../../../../common/constants';
|
||||
import { getTimelinesInStorageByIds } from '../../../timelines/containers/local_storage';
|
||||
import { TimelineId } from '../../../../common/types';
|
||||
import { getDataTablesInStorageByIds } from '../../../timelines/containers/local_storage';
|
||||
import { TableId } from '../../../../common/types';
|
||||
import { getColumns } from '../../../detections/configurations/security_solution_detections';
|
||||
import { useRenderCellValue } from '../../../detections/configurations/security_solution_detections/render_cell_value';
|
||||
import { useToGetInternalFlyout } from '../../../timelines/components/side_panel/event_details/flyout';
|
||||
|
@ -25,8 +25,8 @@ const registerAlertsTableConfiguration = (
|
|||
if (registry.has(APP_ID)) {
|
||||
return;
|
||||
}
|
||||
const timelineStorage = getTimelinesInStorageByIds(storage, [TimelineId.detectionsPage]);
|
||||
const columnsFormStorage = timelineStorage?.[TimelineId.detectionsPage]?.columns ?? [];
|
||||
const dataTableStorage = getDataTablesInStorageByIds(storage, [TableId.alertsOnAlertsPage]);
|
||||
const columnsFormStorage = dataTableStorage?.[TableId.alertsOnAlertsPage]?.columns ?? [];
|
||||
const alertColumns = columnsFormStorage.length ? columnsFormStorage : getColumns();
|
||||
|
||||
registry.register({
|
||||
|
|
|
@ -24,6 +24,7 @@ import type {
|
|||
} from '@testing-library/react-hooks/src/types/react';
|
||||
import type { UseBaseQueryResult } from '@tanstack/react-query';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
import { ConsoleManager } from '../../../management/components/console';
|
||||
import type { StartPlugins, StartServices } from '../../../types';
|
||||
import { depsStartMock } from './dependencies_start_mock';
|
||||
|
@ -204,10 +205,14 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
|
|||
app: experimentalFeaturesReducer,
|
||||
};
|
||||
|
||||
const store = createStore(mockGlobalState, storeReducer, kibanaObservable, storage, [
|
||||
...managementMiddlewareFactory(coreStart, depsStart),
|
||||
middlewareSpy.actionSpyMiddleware,
|
||||
]);
|
||||
const store = createStore(
|
||||
mockGlobalState,
|
||||
storeReducer,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage,
|
||||
[...managementMiddlewareFactory(coreStart, depsStart), middlewareSpy.actionSpyMiddleware]
|
||||
);
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
|
|
|
@ -29,7 +29,13 @@ import {
|
|||
DEFAULT_SIGNALS_INDEX,
|
||||
} from '../../../common/constants';
|
||||
import { networkModel } from '../../network/store';
|
||||
import { TimelineType, TimelineStatus, TimelineTabs } from '../../../common/types/timeline';
|
||||
import {
|
||||
TimelineType,
|
||||
TimelineStatus,
|
||||
TimelineTabs,
|
||||
TableId,
|
||||
TimelineId,
|
||||
} from '../../../common/types/timeline';
|
||||
import { mockManagementState } from '../../management/store/reducer';
|
||||
import type { ManagementState } from '../../management/types';
|
||||
import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model';
|
||||
|
@ -300,15 +306,14 @@ export const mockGlobalState: State = {
|
|||
newTimelineModel: null,
|
||||
},
|
||||
timelineById: {
|
||||
test: {
|
||||
[TimelineId.test]: {
|
||||
activeTab: TimelineTabs.query,
|
||||
prevActiveTab: TimelineTabs.notes,
|
||||
dataViewId: DEFAULT_DATA_VIEW_ID,
|
||||
deletedEventIds: [],
|
||||
documentType: '',
|
||||
queryFields: [],
|
||||
selectAll: false,
|
||||
id: 'test',
|
||||
id: TimelineId.test,
|
||||
savedObjectId: null,
|
||||
columns: defaultHeaders,
|
||||
defaultColumns: defaultHeaders,
|
||||
|
@ -328,7 +333,6 @@ export const mockGlobalState: State = {
|
|||
historyIds: [],
|
||||
isFavorite: false,
|
||||
isLive: false,
|
||||
isSelectAllChecked: false,
|
||||
isLoading: false,
|
||||
kqlMode: 'filter',
|
||||
kqlQuery: { filterQuery: null },
|
||||
|
@ -342,10 +346,8 @@ export const mockGlobalState: State = {
|
|||
start: '2020-07-07T08:20:18.966Z',
|
||||
end: '2020-07-08T08:20:18.966Z',
|
||||
},
|
||||
selectedEventIds: {},
|
||||
sessionViewConfig: null,
|
||||
show: false,
|
||||
showCheckboxes: false,
|
||||
pinnedEventIds: {},
|
||||
pinnedEventsSaveObject: {},
|
||||
itemsPerPageOptions: [5, 10, 20],
|
||||
|
@ -360,10 +362,48 @@ export const mockGlobalState: State = {
|
|||
isSaving: false,
|
||||
version: null,
|
||||
status: TimelineStatus.active,
|
||||
isSelectAllChecked: false,
|
||||
selectedEventIds: {},
|
||||
},
|
||||
},
|
||||
insertTimeline: null,
|
||||
},
|
||||
dataTable: {
|
||||
tableById: {
|
||||
[TableId.test]: {
|
||||
columns: defaultHeaders,
|
||||
defaultColumns: defaultHeaders,
|
||||
dataViewId: 'security-solution-default',
|
||||
deletedEventIds: [],
|
||||
expandedDetail: {},
|
||||
filters: [],
|
||||
indexNames: ['.alerts-security.alerts-default'],
|
||||
isSelectAllChecked: false,
|
||||
itemsPerPage: 25,
|
||||
itemsPerPageOptions: [10, 25, 50, 100],
|
||||
loadingEventIds: [],
|
||||
selectedEventIds: {},
|
||||
showCheckboxes: false,
|
||||
sort: [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
columnType: 'date',
|
||||
esTypes: ['date'],
|
||||
sortDirection: 'desc',
|
||||
},
|
||||
],
|
||||
graphEventId: '',
|
||||
sessionViewConfig: null,
|
||||
selectAll: false,
|
||||
id: TableId.test,
|
||||
title: '',
|
||||
initialized: true,
|
||||
updated: 1663882629000,
|
||||
isLoading: false,
|
||||
queryFields: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
sourcerer: {
|
||||
...mockSourcererState,
|
||||
defaultDataView: {
|
||||
|
|
|
@ -18,6 +18,7 @@ import { ThemeProvider } from 'styled-components';
|
|||
import type { Capabilities } from '@kbn/core/public';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
import { ConsoleManager } from '../../management/components/console';
|
||||
import type { State } from '../store';
|
||||
import { createStore } from '../store';
|
||||
|
@ -52,7 +53,13 @@ const { storage } = createSecuritySolutionStorageMock();
|
|||
/** A utility for wrapping children in the providers required to run most tests */
|
||||
export const TestProvidersComponent: React.FC<Props> = ({
|
||||
children,
|
||||
store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage),
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
),
|
||||
onDragEnd = jest.fn(),
|
||||
}) => {
|
||||
const queryClient = new QueryClient();
|
||||
|
@ -79,7 +86,13 @@ export const TestProvidersComponent: React.FC<Props> = ({
|
|||
*/
|
||||
const TestProvidersWithPrivilegesComponent: React.FC<Props> = ({
|
||||
children,
|
||||
store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage),
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
),
|
||||
onDragEnd = jest.fn(),
|
||||
}) => (
|
||||
<I18nProvider>
|
||||
|
|
|
@ -21,6 +21,7 @@ import { Direction } from '../../../common/search_strategy';
|
|||
import type { CreateTimelineProps } from '../../detections/components/alerts_table/types';
|
||||
import type { TimelineModel } from '../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../timelines/store/timeline/defaults';
|
||||
import type { TGridModel } from '../store/data_table/model';
|
||||
|
||||
export const mockOpenTimelineQueryResults = {
|
||||
totalCount: 11,
|
||||
|
@ -2010,11 +2011,9 @@ export const mockTimelineModel: TimelineModel = {
|
|||
pinnedEventIds: {},
|
||||
pinnedEventsSaveObject: {},
|
||||
savedObjectId: 'ef579e40-jibber-jabber',
|
||||
selectAll: false,
|
||||
selectedEventIds: {},
|
||||
sessionViewConfig: null,
|
||||
show: false,
|
||||
showCheckboxes: false,
|
||||
sort: [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
|
@ -2031,6 +2030,51 @@ export const mockTimelineModel: TimelineModel = {
|
|||
version: '1',
|
||||
};
|
||||
|
||||
export const mockTGridModel: TGridModel = {
|
||||
columns: mockTimelineModelColumns,
|
||||
defaultColumns: mockTimelineModelColumns,
|
||||
dataViewId: null,
|
||||
deletedEventIds: [],
|
||||
expandedDetail: {},
|
||||
filters: [
|
||||
{
|
||||
$state: {
|
||||
store: FilterStateStore.APP_STATE,
|
||||
},
|
||||
meta: {
|
||||
alias: null,
|
||||
disabled: true,
|
||||
key: 'host.name',
|
||||
negate: false,
|
||||
params: '"{"query":"placeholder"}"',
|
||||
type: 'phrase',
|
||||
},
|
||||
query: { match_phrase: { 'host.name': 'placeholder' } },
|
||||
},
|
||||
],
|
||||
id: 'ef579e40-jibber-jabber',
|
||||
indexNames: [],
|
||||
isLoading: false,
|
||||
isSelectAllChecked: false,
|
||||
queryFields: [],
|
||||
itemsPerPage: 25,
|
||||
itemsPerPageOptions: [10, 25, 50, 100],
|
||||
loadingEventIds: [],
|
||||
selectedEventIds: {},
|
||||
sessionViewConfig: null,
|
||||
sort: [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
columnType: 'date',
|
||||
esTypes: ['date'],
|
||||
sortDirection: Direction.desc,
|
||||
},
|
||||
],
|
||||
title: 'Test rule',
|
||||
showCheckboxes: false,
|
||||
selectAll: false,
|
||||
};
|
||||
|
||||
export const mockGetOneTimelineResult: TimelineResult = {
|
||||
savedObjectId: 'ef579e40-jibber-jabber',
|
||||
columns: timelineDefaults.columns.filter((column) => column.id !== 'event.action'),
|
||||
|
@ -2139,11 +2183,9 @@ export const defaultTimelineProps: CreateTimelineProps = {
|
|||
pinnedEventsSaveObject: {},
|
||||
queryFields: [],
|
||||
savedObjectId: null,
|
||||
selectAll: false,
|
||||
selectedEventIds: {},
|
||||
sessionViewConfig: null,
|
||||
show: false,
|
||||
showCheckboxes: false,
|
||||
sort: [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
|
|
|
@ -5,11 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AnyAction, Reducer } from 'redux';
|
||||
import reduceReducers from 'reduce-reducers';
|
||||
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
|
||||
import { hostsReducer } from '../../hosts/store';
|
||||
import { networkReducer } from '../../network/store';
|
||||
import { usersReducer } from '../../users/store';
|
||||
|
@ -17,9 +12,6 @@ import { timelineReducer } from '../../timelines/store/timeline/reducer';
|
|||
import { managementReducer } from '../../management/store/reducer';
|
||||
import type { ManagementPluginReducer } from '../../management';
|
||||
import type { SubPluginsInitReducer } from '../store';
|
||||
import { mockGlobalState } from './global_state';
|
||||
import type { TimelineState } from '../../timelines/store/timeline/types';
|
||||
import { defaultHeaders } from '../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
|
||||
type GlobalThis = typeof globalThis;
|
||||
interface Global extends GlobalThis {
|
||||
|
@ -31,33 +23,11 @@ interface Global extends GlobalThis {
|
|||
|
||||
export const globalNode: Global = global;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const combineTimelineReducer = reduceReducers<any>(
|
||||
{
|
||||
...mockGlobalState.timeline,
|
||||
timelineById: {
|
||||
...mockGlobalState.timeline.timelineById,
|
||||
test: {
|
||||
...mockGlobalState.timeline.timelineById.test,
|
||||
defaultColumns: defaultHeaders,
|
||||
loadingText: 'events',
|
||||
footerText: 'events',
|
||||
documentType: '',
|
||||
selectAll: false,
|
||||
queryFields: [],
|
||||
unit: (n: number) => n,
|
||||
},
|
||||
},
|
||||
},
|
||||
tGridReducer,
|
||||
timelineReducer
|
||||
) as Reducer<TimelineState, AnyAction>;
|
||||
|
||||
export const SUB_PLUGINS_REDUCER: SubPluginsInitReducer = {
|
||||
hosts: hostsReducer,
|
||||
network: networkReducer,
|
||||
users: usersReducer,
|
||||
timeline: combineTimelineReducer,
|
||||
timeline: timelineReducer,
|
||||
/**
|
||||
* These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture,
|
||||
* they are cast to mutable versions here.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
applyDeltaToColumnWidth,
|
||||
clearEventsDeleted,
|
||||
clearEventsLoading,
|
||||
clearSelected,
|
||||
initializeTGridSettings,
|
||||
removeColumn,
|
||||
setEventsDeleted,
|
||||
setEventsLoading,
|
||||
setSelected,
|
||||
setTGridSelectAll,
|
||||
toggleDetailPanel,
|
||||
updateColumnOrder,
|
||||
updateColumns,
|
||||
updateColumnWidth,
|
||||
updateIsLoading,
|
||||
updateItemsPerPage,
|
||||
updateItemsPerPageOptions,
|
||||
updateSort,
|
||||
upsertColumn,
|
||||
updateGraphEventId,
|
||||
updateSessionViewConfig,
|
||||
createTGrid,
|
||||
} from '@kbn/timelines-plugin/public';
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { defaultHeaders } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import type { SubsetTGridModel } from './model';
|
||||
|
||||
export const tableDefaults: SubsetTGridModel = {
|
||||
defaultColumns: defaultHeaders,
|
||||
dataViewId: null,
|
||||
deletedEventIds: [],
|
||||
expandedDetail: {},
|
||||
filters: [],
|
||||
indexNames: [],
|
||||
isSelectAllChecked: false,
|
||||
isLoading: false,
|
||||
itemsPerPage: 25,
|
||||
itemsPerPageOptions: [10, 25, 50, 100],
|
||||
loadingEventIds: [],
|
||||
selectedEventIds: {},
|
||||
showCheckboxes: false,
|
||||
sort: [
|
||||
{
|
||||
columnId: '@timestamp',
|
||||
columnType: 'date',
|
||||
esTypes: ['date'],
|
||||
sortDirection: 'desc',
|
||||
},
|
||||
],
|
||||
graphEventId: '',
|
||||
sessionViewConfig: null,
|
||||
columns: defaultHeaders,
|
||||
queryFields: [],
|
||||
};
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
// we don't have the types for waitFor just yet, so using "as waitFor" for when we do
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import '../../mock/match_media';
|
||||
import {
|
||||
mockGlobalState,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
TestProviders,
|
||||
defaultHeaders,
|
||||
createSecuritySolutionStorageMock,
|
||||
kibanaObservable,
|
||||
} from '../../mock';
|
||||
|
||||
import type { State } from '..';
|
||||
import { createStore } from '..';
|
||||
import {
|
||||
removeColumn,
|
||||
upsertColumn,
|
||||
applyDeltaToColumnWidth,
|
||||
updateColumnOrder,
|
||||
updateColumns,
|
||||
updateColumnWidth,
|
||||
updateItemsPerPage,
|
||||
updateSort,
|
||||
} from './actions';
|
||||
import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
|
||||
import type { Props as StatefulEventsViewerProps } from '../../components/events_viewer';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
|
||||
import { addTableInStorage } from '../../../timelines/containers/local_storage';
|
||||
import { Direction } from '../../../../common/search_strategy';
|
||||
import { tGridReducer } from '@kbn/timelines-plugin/public';
|
||||
import { StatefulEventsViewer } from '../../components/events_viewer';
|
||||
import { eventsDefaultModel } from '../../components/events_viewer/default_model';
|
||||
import { defaultCellActions } from '../../lib/cell_actions/default_cell_actions';
|
||||
import { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { SourcererScopeName } from '../sourcerer/model';
|
||||
import { TableId } from '../../../../common/types';
|
||||
|
||||
jest.mock('../../../timelines/containers/local_storage');
|
||||
|
||||
const addTableInStorageMock = addTableInStorage as jest.Mock;
|
||||
|
||||
describe('epicLocalStorage', () => {
|
||||
const state: State = mockGlobalState;
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
let store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
|
||||
let testProps = {} as StatefulEventsViewerProps;
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore(
|
||||
state,
|
||||
SUB_PLUGINS_REDUCER,
|
||||
{ dataTable: tGridReducer },
|
||||
kibanaObservable,
|
||||
storage
|
||||
);
|
||||
const from = '2019-08-27T22:10:56.794Z';
|
||||
const to = '2019-08-26T22:10:56.791Z';
|
||||
const ACTION_BUTTON_COUNT = 4;
|
||||
|
||||
testProps = {
|
||||
defaultCellActions,
|
||||
defaultModel: eventsDefaultModel,
|
||||
end: to,
|
||||
entityType: EntityType.ALERTS,
|
||||
tableId: TableId.test,
|
||||
leadingControlColumns: getDefaultControlColumn(ACTION_BUTTON_COUNT),
|
||||
renderCellValue: DefaultCellRenderer,
|
||||
rowRenderers: defaultRowRenderers,
|
||||
scopeId: SourcererScopeName.default,
|
||||
start: from,
|
||||
};
|
||||
});
|
||||
|
||||
it('persist adding / reordering of a column correctly', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(upsertColumn({ id: TableId.test, index: 1, column: defaultHeaders[0] }));
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persist timeline when removing a column ', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(removeColumn({ id: TableId.test, columnId: '@timestamp' }));
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persists resizing of a column', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(
|
||||
applyDeltaToColumnWidth({ id: TableId.test, columnId: '@timestamp', delta: 80 })
|
||||
);
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persist the resetting of the fields', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(updateColumns({ id: TableId.test, columns: defaultHeaders }));
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persist items per page', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(updateItemsPerPage({ id: TableId.test, itemsPerPage: 50 }));
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persist the sorting of a column', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(
|
||||
updateSort({
|
||||
id: TableId.test,
|
||||
sort: [
|
||||
{
|
||||
columnId: 'event.severity',
|
||||
columnType: 'number',
|
||||
esTypes: ['long'],
|
||||
sortDirection: Direction.desc,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persists updates to the column order to local storage', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(
|
||||
updateColumnOrder({
|
||||
columnIds: ['event.severity', '@timestamp', 'event.category'],
|
||||
id: TableId.test,
|
||||
})
|
||||
);
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('persists updates to the column width to local storage', async () => {
|
||||
shallow(
|
||||
<TestProviders store={store}>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
store.dispatch(
|
||||
updateColumnWidth({
|
||||
columnId: 'event.severity',
|
||||
id: TableId.test,
|
||||
width: 123,
|
||||
})
|
||||
);
|
||||
await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue