Skip to content

Commit

Permalink
[SIEM] Add SavedQuery in Timeline (#49813) (#50932)
Browse files Browse the repository at this point in the history
* Step-1: Add Search Bar in timeline instead of our own kql

* Step-2: Add the saved query with filter in timeline savedObject

* fix type

* Fix unit test

* fix bug when you use an exists filter

* Fix bug to do a search when add filter by itself

* Review I

* unit tests

* fix import for Filter

* add range as a filter

* remove comment

* forget to add range in ES mapping + allow query with only filters

* fix and/or with filter

* review with Liza
  • Loading branch information
XavierM authored Nov 18, 2019
1 parent 4a336be commit 63b5fbe
Show file tree
Hide file tree
Showing 57 changed files with 3,434 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,11 @@ function QueryBarTopRowUI(props: Props) {

const queryLanguage = props.query && props.query.language;
const persistedLog: PersistedLog | undefined = React.useMemo(
() => (queryLanguage ? getQueryLog(uiSettings!, storage, appName, queryLanguage) : undefined),
[queryLanguage]
() =>
queryLanguage && uiSettings && storage && appName
? getQueryLog(uiSettings!, storage, appName, queryLanguage)
: undefined,
[appName, queryLanguage, uiSettings, storage]
);

function onClickSubmitButton(event: React.MouseEvent<HTMLButtonElement>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface SearchBarOwnProps {
// Show when user has privileges to save
showSaveQuery?: boolean;
savedQuery?: SavedQuery;
onQueryChange?: (payload: { dateRange: TimeRange; query?: Query }) => void;
onQuerySubmit?: (payload: { dateRange: TimeRange; query?: Query }) => void;
// User has saved the current state as a saved query
onSaved?: (savedQuery: SavedQuery) => void;
Expand Down Expand Up @@ -210,6 +211,18 @@ class SearchBarUI extends Component<SearchBarProps, State> {
);
}

/*
* This Function is here to show the toggle in saved query form
* in case you the date range (from/to)
*/
private shouldRenderTimeFilterInSavedQueryForm() {
const { dateRangeFrom, dateRangeTo, showDatePicker } = this.props;
return (
showDatePicker ||
(!showDatePicker && dateRangeFrom !== undefined && dateRangeTo !== undefined)
);
}

public setFilterBarHeight = () => {
requestAnimationFrame(() => {
const height =
Expand Down Expand Up @@ -303,6 +316,9 @@ class SearchBarUI extends Component<SearchBarProps, State> {
dateRangeFrom: queryAndDateRange.dateRange.from,
dateRangeTo: queryAndDateRange.dateRange.to,
});
if (this.props.onQueryChange) {
this.props.onQueryChange(queryAndDateRange);
}
};

public onQueryBarSubmit = (queryAndDateRange: { dateRange?: TimeRange; query?: Query }) => {
Expand Down Expand Up @@ -444,7 +460,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
onSave={this.onSave}
onClose={() => this.setState({ showSaveQueryModal: false })}
showFilterOption={this.props.showFilterBar}
showTimeFilterOption={this.props.showDatePicker}
showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()}
/>
) : null}
{this.state.showSaveNewQueryModal ? (
Expand All @@ -453,7 +469,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
onSave={savedQueryMeta => this.onSave(savedQueryMeta, true)}
onClose={() => this.setState({ showSaveNewQueryModal: false })}
showFilterOption={this.props.showFilterBar}
showTimeFilterOption={this.props.showDatePicker}
showTimeFilterOption={this.shouldRenderTimeFilterInSavedQueryForm()}
/>
) : null}
</div>
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/data/common/es_query/filters/meta_filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@ export interface FilterValueFormatter {
}

export interface FilterMeta {
alias: string | null;
disabled: boolean;
negate: boolean;
// controlledBy is there to identify who owns the filter
controlledBy?: string;
// index and type are optional only because when you create a new filter, there are no defaults
index?: string;
type?: string;
disabled: boolean;
negate: boolean;
alias: string | null;
key?: string;
value?: string | ((formatter?: FilterValueFormatter) => string);
params?: any;
value?: string | ((formatter?: FilterValueFormatter) => string);
}

export interface Filter {
Expand Down
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/siem/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const DEFAULT_TO = 'now';
export const DEFAULT_INTERVAL_PAUSE = true;
export const DEFAULT_INTERVAL_TYPE = 'manual';
export const DEFAULT_INTERVAL_VALUE = 300000; // ms
export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges';

/**
* Id for the SIGNALS alerting type
Expand Down
5 changes: 4 additions & 1 deletion x-pack/legacy/plugins/siem/public/apps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ import { npStart } from 'ui/new_platform';
import { Plugin } from './plugin';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
new Plugin({ opaqueId: Symbol('siem'), env: {} as any }, chrome).start(npStart);
new Plugin({ opaqueId: Symbol('siem'), env: {} as any }, chrome).start(
npStart.core,
npStart.plugins
);
8 changes: 1 addition & 7 deletions x-pack/legacy/plugins/siem/public/apps/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ import template from './template.html';

export const ROOT_ELEMENT_ID = 'react-siem-root';

export interface StartObject {
core: LegacyCoreStart;
plugins: PluginsStart;
}

export class Plugin {
constructor(
// @ts-ignore this is added to satisfy the New Platform typing constraint,
Expand All @@ -30,8 +25,7 @@ export class Plugin {
this.chrome = chrome;
}

public start(start: StartObject): void {
const { core, plugins } = start;
public start(core: LegacyCoreStart, plugins: PluginsStart) {
// @ts-ignore improper type description
this.chrome.setRootTemplate(template);
const checkForRoot = () => {
Expand Down
32 changes: 23 additions & 9 deletions x-pack/legacy/plugins/siem/public/apps/start_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import React, { memo, FC } from 'react';
import { ApolloProvider } from 'react-apollo';
import { Provider as ReduxStoreProvider } from 'react-redux';
import { ThemeProvider } from 'styled-components';
import { LegacyCoreStart } from 'kibana/public';
import { PluginsStart } from 'ui/new_platform/new_platform';

import { EuiErrorBoundary } from '@elastic/eui';
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
Expand All @@ -17,6 +19,9 @@ import { BehaviorSubject } from 'rxjs';
import { pluck } from 'rxjs/operators';
import { I18nContext } from 'ui/i18n';

import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';

import { DEFAULT_DARK_MODE } from '../../common/constants';
import { ErrorToastDispatcher } from '../components/error_toast_dispatcher';
import { compose } from '../lib/compose/kibana_compose';
Expand All @@ -31,8 +36,6 @@ import { MlCapabilitiesProvider } from '../components/ml/permissions/ml_capabili

import { ApolloClientContext } from '../utils/apollo_context';

import { StartObject } from './plugin';

const StartApp: FC<AppFrontendLibs> = memo(libs => {
const history = createHashHistory();

Expand Down Expand Up @@ -74,10 +77,21 @@ const StartApp: FC<AppFrontendLibs> = memo(libs => {

export const ROOT_ELEMENT_ID = 'react-siem-root';

export const SiemApp = memo<StartObject>(({ core, plugins }) => (
<KibanaCoreContextProvider core={core}>
<KibanaPluginsContextProvider plugins={plugins}>
<StartApp {...compose()} />
</KibanaPluginsContextProvider>
</KibanaCoreContextProvider>
));
export const SiemApp = memo<{ core: LegacyCoreStart; plugins: PluginsStart }>(
({ core, plugins }) => (
<KibanaContextProvider
services={{
appName: 'siem',
data: plugins.data,
storage: new Storage(localStorage),
...core,
}}
>
<KibanaCoreContextProvider core={core}>
<KibanaPluginsContextProvider plugins={plugins}>
<StartApp {...compose()} />
</KibanaPluginsContextProvider>
</KibanaCoreContextProvider>
</KibanaContextProvider>
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { FlyoutButton } from './button';
const testFlyoutHeight = 980;
const usersViewing = ['elastic'];

jest.mock('../../lib/settings/use_kibana_ui_setting');

describe('Flyout', () => {
const state: State = mockGlobalState;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ import 'jest-styled-components';
import * as React from 'react';

import { flyoutHeaderHeight } from '../';
import { useKibanaCore } from '../../../lib/compose/kibana_core';
import { TestProviders } from '../../../mock';

import { mockUiSettings } from '../../../mock/ui_settings';
import { Pane } from '.';

const testFlyoutHeight = 980;
const testWidth = 640;
const usersViewing = ['elastic'];

const mockUseKibanaCore = useKibanaCore as jest.Mock;
jest.mock('../../../lib/compose/kibana_core');
mockUseKibanaCore.mockImplementation(() => ({
uiSettings: mockUiSettings,
}));

describe('Pane', () => {
test('renders correctly against snapshot', () => {
const EmptyComponent = shallow(
Expand Down
Loading

0 comments on commit 63b5fbe

Please sign in to comment.