Skip to content

Commit

Permalink
[8.13] [Security Solution] Incorporates EQL options in EQL query vali…
Browse files Browse the repository at this point in the history
…dation on both Rule Creation and Timeline (#178468) (#178994)

# Backport

This will backport the following commits from `main` to `8.13`:
- [[Security Solution] Incorporates EQL options in EQL query validation
on both Rule Creation and Timeline
(#178468)](#178468)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ryland
Herrick","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-03-19T17:52:35Z","message":"[Security
Solution] Incorporates EQL options in EQL query validation on both Rule
Creation and Timeline (#178468)\n\n## Summary\r\n\r\nThis PR updates the
Detection Rule Creation and Timeline forms to\r\naccount for the new(er)
EQL options fields (`timestamp_field`,\r\n`event_category_field`, and
`tiebreaker_field`) when validating the EQL\r\nquery. While the rule
query and timeline query (respectively) would\r\ncorrectly persist and
use these options, they were unused during EQL\r\nvalidation, meaning
that in certain situations it was impossible to\r\nproduce a \"valid\"
(from the perspective of the form) EQL query if your\r\ndata
necessitated one of those options
(see\r\nhttps://github.com//issues/158326 for more
details).\r\n\r\n### Changes\r\nThe above was accomplished by:\r\n\r\n1.
Adding a hidden form field to the UI for `eqlOptions`\r\n* A lack of a
field component meant that `formData` never held any value\r\nfor this
field\r\n* This meant that the form-based validations (i.e.
`eqlValidator`) would\r\nnot have access to `eqlOptions`\r\n2. Updating
the `onOptionsChange` hook to additionally inform the form\r\nof changes
to `eqlOptions`\r\n* This allows the form to have updated values for
`eqlOptions` with\r\nminimal changes\r\n* Not ideal from a data-flow
perspective, since the field changes are\r\nupdating two copies of
`eqlOptions`. However, we're only using the \"form\r\ncopy\" for the
validation call, and everything else works as it did\r\nbefore.\r\n\r\n
\r\n### Additional changes\r\n__Update__: I've reverted the described
changes to the default timeline\r\nvalues below, and moved that to a
[separate\r\nissue](https://github.com/elastic/security-team/issues/8892).\r\n\r\nIn
the course of performing the above tasks for timeline, I found
the\r\nbehavior there to be slightly different: we were specifying
default\r\nvalues for these fields, which was redundant in some
cases\r\n(`timestamp_field`, `event_category_field`), and invalid in the
case of\r\n`tiebreaker_field`. You can see here how the
`tiebreaker_field` must be\r\nchanged to make the query
valid:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/556bc998-c147-4825-8bde-5d8d03873e75\r\n\r\n\r\n~~I
opted to remove these default values, as well as the
defaulting\r\nbehavior from the search strategy call
(see\r\n10a47fdaba5860a44638aada78c0cf8c37b2c580). While these values
may be\r\npersisted in existing saved timelines, this makes the EQL
workflow much\r\ncleaner for new users, and those users with persisted
EQL options can\r\nnow modify and validate those options on the
fly.~~\r\n\r\n#### Steps to Review\r\n\r\nFor
@elastic/kibana-visualizations and/or
@elastic/kibana-data-discovery\r\n: I've added a few tests to the EQL
search strategy that document\r\nexisting behavior RE EQL option fields.
Nothing about the search\r\nstrategy itself was changed. If you think
the tests aren't useful, I'm\r\nhappy to delete them.\r\n\r\n1. Create
an index without a `@timestamp` field, and insert some data:\r\n
<details>\r\n <summary>Dev Tools</summary>\r\n\r\n PUT timestamp/\r\n
{\r\n \"mappings\": {\r\n \"dynamic\": \"strict\",\r\n \"properties\":
{\r\n \"timestamp\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"locale\": {\r\n \"type\": \"keyword\"\r\n
},\r\n \"created_at\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"event\": {\r\n \"properties\": {\r\n
\"category\": {\r\n \"ignore_above\": 1024,\r\n \"type\":
\"keyword\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n PUT
timestamp/_doc/4\r\n {\r\n \"timestamp\": 1710211630,\r\n \"locale\":
\"es\",\r\n \"created_at\": 1710211630,\r\n \"event.category\":
\"configuration\"\r\n }\r\n </details>\r\n2. Create a new EQL detection
rule, and choose `timestamp` as the index\r\npattern\r\n3. Type `any
where true` as the query, and observe a validation error\r\n(`Found 1
problem line -1:-1: Unknown column [@timestamp]`)\r\n4. Open EQL
Settings popover, and select `timestamp` from the `Timestamp\r\nfield`
dropdown\r\n5. Observe that the query has been revalidated, and found
valid.\r\n\r\n\r\n### New
Behavior:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/22a63363-d597-4dfd-8f9a-647f4084ec0e\r\n\r\n\r\n\r\n###
Related Issues\r\n* Addresses
https://github.com/elastic/kibana/issues/158326\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n### For maintainers\r\n\r\n- [x]
This was checked for breaking API changes and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"5be91c94b1c7e6b1aebd5fe75c86e2bc17c90ad0","branchLabelMapping":{"^v8.14.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Feature:Timeline","Feature:Event
Correlation (EQL) Rule","Feature:Rule
Creation","backport:prev-minor","Team:Detection
Engine","v8.14.0"],"title":"[Security Solution] Incorporates EQL options
in EQL query validation on both Rule Creation and
Timeline","number":178468,"url":"https://github.com/elastic/kibana/pull/178468","mergeCommit":{"message":"[Security
Solution] Incorporates EQL options in EQL query validation on both Rule
Creation and Timeline (#178468)\n\n## Summary\r\n\r\nThis PR updates the
Detection Rule Creation and Timeline forms to\r\naccount for the new(er)
EQL options fields (`timestamp_field`,\r\n`event_category_field`, and
`tiebreaker_field`) when validating the EQL\r\nquery. While the rule
query and timeline query (respectively) would\r\ncorrectly persist and
use these options, they were unused during EQL\r\nvalidation, meaning
that in certain situations it was impossible to\r\nproduce a \"valid\"
(from the perspective of the form) EQL query if your\r\ndata
necessitated one of those options
(see\r\nhttps://github.com//issues/158326 for more
details).\r\n\r\n### Changes\r\nThe above was accomplished by:\r\n\r\n1.
Adding a hidden form field to the UI for `eqlOptions`\r\n* A lack of a
field component meant that `formData` never held any value\r\nfor this
field\r\n* This meant that the form-based validations (i.e.
`eqlValidator`) would\r\nnot have access to `eqlOptions`\r\n2. Updating
the `onOptionsChange` hook to additionally inform the form\r\nof changes
to `eqlOptions`\r\n* This allows the form to have updated values for
`eqlOptions` with\r\nminimal changes\r\n* Not ideal from a data-flow
perspective, since the field changes are\r\nupdating two copies of
`eqlOptions`. However, we're only using the \"form\r\ncopy\" for the
validation call, and everything else works as it did\r\nbefore.\r\n\r\n
\r\n### Additional changes\r\n__Update__: I've reverted the described
changes to the default timeline\r\nvalues below, and moved that to a
[separate\r\nissue](https://github.com/elastic/security-team/issues/8892).\r\n\r\nIn
the course of performing the above tasks for timeline, I found
the\r\nbehavior there to be slightly different: we were specifying
default\r\nvalues for these fields, which was redundant in some
cases\r\n(`timestamp_field`, `event_category_field`), and invalid in the
case of\r\n`tiebreaker_field`. You can see here how the
`tiebreaker_field` must be\r\nchanged to make the query
valid:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/556bc998-c147-4825-8bde-5d8d03873e75\r\n\r\n\r\n~~I
opted to remove these default values, as well as the
defaulting\r\nbehavior from the search strategy call
(see\r\n10a47fdaba5860a44638aada78c0cf8c37b2c580). While these values
may be\r\npersisted in existing saved timelines, this makes the EQL
workflow much\r\ncleaner for new users, and those users with persisted
EQL options can\r\nnow modify and validate those options on the
fly.~~\r\n\r\n#### Steps to Review\r\n\r\nFor
@elastic/kibana-visualizations and/or
@elastic/kibana-data-discovery\r\n: I've added a few tests to the EQL
search strategy that document\r\nexisting behavior RE EQL option fields.
Nothing about the search\r\nstrategy itself was changed. If you think
the tests aren't useful, I'm\r\nhappy to delete them.\r\n\r\n1. Create
an index without a `@timestamp` field, and insert some data:\r\n
<details>\r\n <summary>Dev Tools</summary>\r\n\r\n PUT timestamp/\r\n
{\r\n \"mappings\": {\r\n \"dynamic\": \"strict\",\r\n \"properties\":
{\r\n \"timestamp\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"locale\": {\r\n \"type\": \"keyword\"\r\n
},\r\n \"created_at\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"event\": {\r\n \"properties\": {\r\n
\"category\": {\r\n \"ignore_above\": 1024,\r\n \"type\":
\"keyword\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n PUT
timestamp/_doc/4\r\n {\r\n \"timestamp\": 1710211630,\r\n \"locale\":
\"es\",\r\n \"created_at\": 1710211630,\r\n \"event.category\":
\"configuration\"\r\n }\r\n </details>\r\n2. Create a new EQL detection
rule, and choose `timestamp` as the index\r\npattern\r\n3. Type `any
where true` as the query, and observe a validation error\r\n(`Found 1
problem line -1:-1: Unknown column [@timestamp]`)\r\n4. Open EQL
Settings popover, and select `timestamp` from the `Timestamp\r\nfield`
dropdown\r\n5. Observe that the query has been revalidated, and found
valid.\r\n\r\n\r\n### New
Behavior:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/22a63363-d597-4dfd-8f9a-647f4084ec0e\r\n\r\n\r\n\r\n###
Related Issues\r\n* Addresses
https://github.com/elastic/kibana/issues/158326\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n### For maintainers\r\n\r\n- [x]
This was checked for breaking API changes and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"5be91c94b1c7e6b1aebd5fe75c86e2bc17c90ad0"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.14.0","branchLabelMappingKey":"^v8.14.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/178468","number":178468,"mergeCommit":{"message":"[Security
Solution] Incorporates EQL options in EQL query validation on both Rule
Creation and Timeline (#178468)\n\n## Summary\r\n\r\nThis PR updates the
Detection Rule Creation and Timeline forms to\r\naccount for the new(er)
EQL options fields (`timestamp_field`,\r\n`event_category_field`, and
`tiebreaker_field`) when validating the EQL\r\nquery. While the rule
query and timeline query (respectively) would\r\ncorrectly persist and
use these options, they were unused during EQL\r\nvalidation, meaning
that in certain situations it was impossible to\r\nproduce a \"valid\"
(from the perspective of the form) EQL query if your\r\ndata
necessitated one of those options
(see\r\nhttps://github.com//issues/158326 for more
details).\r\n\r\n### Changes\r\nThe above was accomplished by:\r\n\r\n1.
Adding a hidden form field to the UI for `eqlOptions`\r\n* A lack of a
field component meant that `formData` never held any value\r\nfor this
field\r\n* This meant that the form-based validations (i.e.
`eqlValidator`) would\r\nnot have access to `eqlOptions`\r\n2. Updating
the `onOptionsChange` hook to additionally inform the form\r\nof changes
to `eqlOptions`\r\n* This allows the form to have updated values for
`eqlOptions` with\r\nminimal changes\r\n* Not ideal from a data-flow
perspective, since the field changes are\r\nupdating two copies of
`eqlOptions`. However, we're only using the \"form\r\ncopy\" for the
validation call, and everything else works as it did\r\nbefore.\r\n\r\n
\r\n### Additional changes\r\n__Update__: I've reverted the described
changes to the default timeline\r\nvalues below, and moved that to a
[separate\r\nissue](https://github.com/elastic/security-team/issues/8892).\r\n\r\nIn
the course of performing the above tasks for timeline, I found
the\r\nbehavior there to be slightly different: we were specifying
default\r\nvalues for these fields, which was redundant in some
cases\r\n(`timestamp_field`, `event_category_field`), and invalid in the
case of\r\n`tiebreaker_field`. You can see here how the
`tiebreaker_field` must be\r\nchanged to make the query
valid:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/556bc998-c147-4825-8bde-5d8d03873e75\r\n\r\n\r\n~~I
opted to remove these default values, as well as the
defaulting\r\nbehavior from the search strategy call
(see\r\n10a47fdaba5860a44638aada78c0cf8c37b2c580). While these values
may be\r\npersisted in existing saved timelines, this makes the EQL
workflow much\r\ncleaner for new users, and those users with persisted
EQL options can\r\nnow modify and validate those options on the
fly.~~\r\n\r\n#### Steps to Review\r\n\r\nFor
@elastic/kibana-visualizations and/or
@elastic/kibana-data-discovery\r\n: I've added a few tests to the EQL
search strategy that document\r\nexisting behavior RE EQL option fields.
Nothing about the search\r\nstrategy itself was changed. If you think
the tests aren't useful, I'm\r\nhappy to delete them.\r\n\r\n1. Create
an index without a `@timestamp` field, and insert some data:\r\n
<details>\r\n <summary>Dev Tools</summary>\r\n\r\n PUT timestamp/\r\n
{\r\n \"mappings\": {\r\n \"dynamic\": \"strict\",\r\n \"properties\":
{\r\n \"timestamp\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"locale\": {\r\n \"type\": \"keyword\"\r\n
},\r\n \"created_at\": {\r\n \"type\": \"date\",\r\n \"format\":
\"epoch_second\"\r\n },\r\n \"event\": {\r\n \"properties\": {\r\n
\"category\": {\r\n \"ignore_above\": 1024,\r\n \"type\":
\"keyword\"\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n PUT
timestamp/_doc/4\r\n {\r\n \"timestamp\": 1710211630,\r\n \"locale\":
\"es\",\r\n \"created_at\": 1710211630,\r\n \"event.category\":
\"configuration\"\r\n }\r\n </details>\r\n2. Create a new EQL detection
rule, and choose `timestamp` as the index\r\npattern\r\n3. Type `any
where true` as the query, and observe a validation error\r\n(`Found 1
problem line -1:-1: Unknown column [@timestamp]`)\r\n4. Open EQL
Settings popover, and select `timestamp` from the `Timestamp\r\nfield`
dropdown\r\n5. Observe that the query has been revalidated, and found
valid.\r\n\r\n\r\n### New
Behavior:\r\n\r\n\r\nhttps://github.com/elastic/kibana/assets/657252/22a63363-d597-4dfd-8f9a-647f4084ec0e\r\n\r\n\r\n\r\n###
Related Issues\r\n* Addresses
https://github.com/elastic/kibana/issues/158326\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [x] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n\r\n### For maintainers\r\n\r\n- [x]
This was checked for breaking API changes and was
[labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"5be91c94b1c7e6b1aebd5fe75c86e2bc17c90ad0"}}]}]
BACKPORT-->

---------

Co-authored-by: Ryland Herrick <[email protected]>
  • Loading branch information
kibanamachine and rylnd authored Mar 19, 2024
1 parent 1faa537 commit 5814aa9
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 68 deletions.
5 changes: 3 additions & 2 deletions src/plugins/data/common/search/strategies/eql_search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
* Side Public License, v 1.
*/

import type { EqlSearchRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { EqlSearchRequest } from '@elastic/elasticsearch/lib/api/types';
import type { EqlSearchRequest as EqlSearchRequestWithBody } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { TransportRequestOptions } from '@elastic/elasticsearch';

import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../types';

export const EQL_SEARCH_STRATEGY = 'eql';

export type EqlRequestParams = EqlSearchRequest;
export type EqlRequestParams = EqlSearchRequest | EqlSearchRequestWithBody;

export interface EqlSearchStrategyRequest extends IKibanaSearchRequest<EqlRequestParams> {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ describe('EQL search strategy', () => {
options,
params: {
...params,
// @ts-expect-error not allowed at top level when using `typesWithBodyKey`
wait_for_completion_timeout: '5ms',
keep_on_completion: false,
},
Expand Down Expand Up @@ -277,6 +276,48 @@ describe('EQL search strategy', () => {

expect(requestOptions).toEqual({ ignore: [400], meta: true, signal: undefined });
});

describe('EQL-specific arguments', () => {
it('passes along a timestamp_field argument', async () => {
const eqlSearch = eqlSearchStrategyProvider(mockSearchConfig, mockLogger);
const request: EqlSearchStrategyRequest = {
params: { index: 'all', timestamp_field: 'timestamp' },
};

await firstValueFrom(eqlSearch.search(request, {}, mockDeps));
const [[actualParams]] = mockEqlSearch.mock.calls;

expect(actualParams).toEqual(expect.objectContaining({ timestamp_field: 'timestamp' }));
});

it('passes along an event_category_field argument', async () => {
const eqlSearch = eqlSearchStrategyProvider(mockSearchConfig, mockLogger);
const request: EqlSearchStrategyRequest = {
params: { index: 'all', event_category_field: 'event_category' },
};

await firstValueFrom(eqlSearch.search(request, {}, mockDeps));
const [[actualParams]] = mockEqlSearch.mock.calls;

expect(actualParams).toEqual(
expect.objectContaining({ event_category_field: 'event_category' })
);
});

it('passes along a tiebreaker_field argument', async () => {
const eqlSearch = eqlSearchStrategyProvider(mockSearchConfig, mockLogger);
const request: EqlSearchStrategyRequest = {
params: { index: 'all', tiebreaker_field: 'event_category' },
};

await firstValueFrom(eqlSearch.search(request, {}, mockDeps));
const [[actualParams]] = mockEqlSearch.mock.calls;

expect(actualParams).toEqual(
expect.objectContaining({ tiebreaker_field: 'event_category' })
);
});
});
});

describe('response', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type { EqlSearchStrategyRequest, EqlSearchStrategyResponse } from '@kbn/d
import { EQL_SEARCH_STRATEGY } from '@kbn/data-plugin/common';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

import type { EqlOptionsSelected } from '../../../../common/search_strategy';
import {
getValidationErrors,
isErrorResponse,
Expand All @@ -23,6 +24,7 @@ interface Params {
data: DataPublicPluginStart;
signal: AbortSignal;
runtimeMappings: estypes.MappingRuntimeFields | undefined;
options: Omit<EqlOptionsSelected, 'query' | 'size'> | undefined;
}

export const validateEql = async ({
Expand All @@ -31,13 +33,17 @@ export const validateEql = async ({
query,
signal,
runtimeMappings,
options,
}: Params): Promise<{ valid: boolean; errors: string[] }> => {
const { rawResponse: response } = await firstValueFrom(
data.search.search<EqlSearchStrategyRequest, EqlSearchStrategyResponse>(
{
params: {
index: dataViewTitle,
body: { query, runtime_mappings: runtimeMappings, size: 0 },
timestamp_field: options?.timestampField,
tiebreaker_field: options?.tiebreakerField || undefined,
event_category_field: options?.eventCategoryField,
},
options: { ignore: [400] },
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { mockQueryBar } from '../../../rule_management_ui/components/rules_table
import type { EqlQueryBarProps } from './eql_query_bar';
import { EqlQueryBar } from './eql_query_bar';
import { getEqlValidationError } from './validators.mock';
import { fireEvent, render, within } from '@testing-library/react';

jest.mock('../../../../common/lib/kibana');

Expand Down Expand Up @@ -117,4 +118,38 @@ describe('EqlQueryBar', () => {

expect(wrapper.find('[data-test-subj="eql-validation-errors-popover"]').exists()).toEqual(true);
});

describe('EQL options interaction', () => {
const mockOptionsData = {
keywordFields: [],
dateFields: [{ label: 'timestamp', value: 'timestamp' }],
nonDateFields: [],
};

it('invokes onOptionsChange when the EQL options change', () => {
const onOptionsChangeMock = jest.fn();

const { getByTestId, getByText } = render(
<TestProviders>
<EqlQueryBar
dataTestSubj="myQueryBar"
field={mockField}
isLoading={false}
optionsData={mockOptionsData}
indexPattern={mockIndexPattern}
onOptionsChange={onOptionsChangeMock}
/>
</TestProviders>
);

// open options popover
fireEvent.click(getByTestId('eql-settings-trigger'));
// display combobox options
within(getByTestId(`eql-timestamp-field`)).getByRole('combobox').focus();
// select timestamp
getByText('timestamp').click();

expect(onOptionsChangeMock).toHaveBeenCalledWith('timestampField', 'timestamp');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Subscription } from 'rxjs';
import styled from 'styled-components';
import deepEqual from 'fast-deep-equal';
import { EuiFormRow, EuiSpacer, EuiTextArea } from '@elastic/eui';
import type { DataViewBase, Filter, Query } from '@kbn/es-query';
import type { DataViewBase } from '@kbn/es-query';
import { FilterManager } from '@kbn/data-plugin/public';

import type { FieldHook } from '../../../../shared_imports';
Expand Down Expand Up @@ -58,12 +58,6 @@ const StyledFormRow = styled(EuiFormRow)`
}
`;

export interface FieldValueQueryBar {
filters: Filter[];
query: Query;
saved_id?: string;
}

export interface EqlQueryBarProps {
dataTestSubj: string;
field: FieldHook<DefineStepRule['queryBar']>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const eqlValidator = async (
const [{ value, formData }] = args;
const { query: queryValue } = value as FieldValueQueryBar;
const query = queryValue.query as string;
const { dataViewId, index, ruleType } = formData as DefineStepRule;
const { dataViewId, index, ruleType, eqlOptions } = formData as DefineStepRule;

const needsValidation =
(ruleType === undefined && !isEmpty(query)) || (isEqlRule(ruleType) && !isEmpty(query));
Expand All @@ -82,7 +82,14 @@ export const eqlValidator = async (
}

const signal = new AbortController().signal;
const response = await validateEql({ data, query, signal, dataViewTitle, runtimeMappings });
const response = await validateEql({
data,
query,
signal,
dataViewTitle,
runtimeMappings,
options: eqlOptions,
});

if (response?.valid === false) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,14 @@ import { StepContentWrapper } from '../../../rule_creation/components/step_conte
import { ThresholdInput } from '../threshold_input';
import { SuppressionInfoIcon } from '../suppression_info_icon';
import { EsqlInfoIcon } from '../../../rule_creation/components/esql_info_icon';
import { Field, Form, getUseField, UseField, UseMultiFields } from '../../../../shared_imports';
import {
Field,
Form,
getUseField,
HiddenField,
UseField,
UseMultiFields,
} from '../../../../shared_imports';
import type { FormHook } from '../../../../shared_imports';
import { schema } from './schema';
import { getTermsAggregationFields } from './utils';
Expand Down Expand Up @@ -768,14 +775,20 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
onOpenTimeline,
]
);

const onOptionsChange = useCallback(
(field: FieldsEqlOptions, value: string | undefined) => {
setOptionsSelected((prevOptions) => ({
...prevOptions,
[field]: value,
}));
setOptionsSelected((prevOptions) => {
const newOptions = {
...prevOptions,
[field]: value,
};

setFieldValue('eqlOptions', newOptions);
return newOptions;
});
},
[setOptionsSelected]
[setFieldValue, setOptionsSelected]
);

const optionsData = useMemo(
Expand Down Expand Up @@ -814,17 +827,16 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
<>
<StepContentWrapper addPadding={!isUpdateView}>
<Form form={form} data-test-subj="stepDefineRule">
<StyledVisibleContainer isVisible={false}>
<UseField
path="dataSourceType"
componentProps={{
euiFieldProps: {
fullWidth: true,
placeholder: '',
},
}}
/>
</StyledVisibleContainer>
<UseField
path="dataSourceType"
component={HiddenField}
componentProps={{
euiFieldProps: {
fullWidth: true,
placeholder: '',
},
}}
/>
<UseField
path="ruleType"
component={SelectRuleType}
Expand All @@ -838,29 +850,31 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
</StyledVisibleContainer>
<EuiSpacer size="s" />
{isEqlRule(ruleType) ? (
<UseField
key="EqlQueryBar"
path="queryBar"
component={EqlQueryBar}
componentProps={{
optionsData,
optionsSelected,
isSizeOptionDisabled: true,
onOptionsChange,
onValidityChange: setIsQueryBarValid,
idAria: 'detectionEngineStepDefineRuleEqlQueryBar',
isDisabled: isLoading,
isLoading: isIndexPatternLoading,
indexPattern,
showFilterBar: true,
// isLoading: indexPatternsLoading,
dataTestSubj: 'detectionEngineStepDefineRuleEqlQueryBar',
}}
config={{
...schema.queryBar,
label: i18n.EQL_QUERY_BAR_LABEL,
}}
/>
<>
<UseField
key="EqlQueryBar"
path="queryBar"
component={EqlQueryBar}
componentProps={{
optionsData,
optionsSelected,
isSizeOptionDisabled: true,
onOptionsChange,
onValidityChange: setIsQueryBarValid,
idAria: 'detectionEngineStepDefineRuleEqlQueryBar',
isDisabled: isLoading,
isLoading: isIndexPatternLoading,
indexPattern,
showFilterBar: true,
dataTestSubj: 'detectionEngineStepDefineRuleEqlQueryBar',
}}
config={{
...schema.queryBar,
label: i18n.EQL_QUERY_BAR_LABEL,
}}
/>
<UseField path="eqlOptions" component={HiddenField} />
</>
) : isEsqlRule(ruleType) ? (
EsqlQueryBarMemo
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ export const schema: FormSchema<DefineStepRule> = {
),
validations: [],
},
eqlOptions: {},
eqlOptions: {
fieldsToValidateOnChange: ['eqlOptions', 'queryBar'],
},
queryBar: {
fieldsToValidateOnChange: ['queryBar'],
validations: [
{
validator: (
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/public/shared_imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ export {
useFormData,
VALIDATION_TYPES,
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
export { Field, SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components';
export { Field, SelectField, HiddenField } from '@kbn/es-ui-shared-plugin/static/forms/components';
export { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers';
export type { ERROR_CODE } from '@kbn/es-ui-shared-plugin/static/forms/helpers/field_validators/types';
Loading

0 comments on commit 5814aa9

Please sign in to comment.